Making Buffer for Android more proactive with Reactive-Pusher

making-buffer-android-proactive-reactive-pusher-header.png

The following blogpost is a primer on architecting Android apps with RxJava and Pusher. It was written by Joe Birch, a Senior Android Engineer at Buffer. Buffer is a social media management platform, and a long time Pusher customer. Joe is a Google Developer Expert for Android, renowned blogger and public speaker. You can follow \[…\]

Introduction

The following blogpost is a primer on architecting Android apps with RxJava and Pusher. It was written by Joe Birch, a Senior Android Engineer at Buffer. Buffer is a social media management platform, and a long time Pusher customer. Joe is a Google Developer Expert for Android, renowned blogger and public speaker. You can follow him on Medium, Twitter, or speaking at a conference near you.

When your application content reflects the state of data stored on a server, making sure you’re always displaying the most up-to-date state ensures a better experience for the user. Now, this can be achieved by the user themselves but it involves the manual refreshing of content. For example, the user may be required to pull-to-refresh in order to show the latest content in some form of content feed. In any app this is probably the simpler method to implement, but it’s not the best experience for our users.

And to build on this even further — in the case of Buffer, another team member within your account may have paused your queue, added a new update or rescheduled a post to go out on a different day — when this happens you may not be aware of it and without repeatedly manually refreshing, you would never know. That’s where Pusher comes in to help us out! Now in Buffer for Android (well, when it’s out of beta!), whenever content changes on the server-side the Android app will refresh the content shown to ensure that you’re always seeing the freshest state of content. The flow for this looks something like this:

As shown above, there are four different refresh events which are now triggered in the app:

  • We refresh the queue of updates whenever any events occur related to updates. For example, this could be a new update being added or an existing on edited edited by any other device.
  • If a Draft contribution post is updated or approved on another device, we refresh the queue of Draft posts.
  • If an update is collected, then this means new analytics are available for us to display which means we need to refresh the analytics queue of content.
  • If the profile queue is paused or un-paused on another device then we reflect this state in the app by toggling the visibility of the pause/un-pause queue button.

The team over at Pusher made the process of implementing this in our app quite delightful by providing us with a helpful Java Client Library – pusher/pusher-websocket-java

Now, this is great work and super useful by itself. But the way in which we wanted to use it in our app, and the places where it would be used, meant that it wasn’t the exact representation which we felt was best for our needs. Because of that, we went ahead to create the Reactive-Pusher library – bufferapp/Reactive-Pusher

But why exactly did we decide to wrap the library in the first place?

  • To begin with, both of the apps we work on within the Android team at Buffer heavily use a reactive approach within their architectures. To keep this architecture in place it’s ideal if everything follows the same approaches to ensure a consistent approach.
  • This in turn makes it easier for us to chain events in our app as part of the same observable stream. So now we can subscribe to events from Pusher we can chain on specific actions to be carried out once those events have taken place.
  • Everything is part of the same stream, the reactive implementation allows us to continue this practice making the application less error prone and easier to debug.
  • Following on from consistency, it’s easier to understand and maintain these parts of of our project if they’re built with the same approach as other parts of the project. If someone new was to join the team or an existing developer was to jump into that code, there wouldn’t be any increased friction from the need to learn new things.
  • When we subscribe to pusher events, the startup and tear down of these subscriptions now becomes part of the same process for the other reactive components within the corresponding screens. This reduces the need to keep a track of different instance references as the same CompositeDisposable is responsible for all subscriptions in that screen.

So, what can you do with RxPusher?

RxPusher supports most of the functionality found within the Pusher Java Client library, here’s a quick look at what you can do with it and how:

Connecting to Pusher

You can use the observeConnection() method to observe the connection to the Pusher service, this won’t terminate so you will continue to receive connection updates regardless of how the Pusher connection status changes:

1reactivePusher.observeConnection().subscribe({
2        when (it) {
3            ConnectionStatus.CONNECTED -> { }
4            ConnectionStatus.CONNECTING -> { }
5            ConnectionStatus.DISCONNECTING -> { }
6            ConnectionStatus.DISCONNECTED -> { }
7            ConnectionStatus.RECONNECTING -> { }
8            ConnectionStatus.UNKNOWN -> { }
9        }
10    }))

There is also an observeConnection(varargs filter: String) method available that allows you to pass a collection of ConnectionEvents which you wish to exclude the callback being triggered.

Getting channels

You can retrieve channels using either the getChannel(), getPrivateChannel()or getPresenceChannel() methods.
Calling getChannels() will return you an instance of a Channel, getPrivateChannel() a PrivateChannel instance and getPresenceChannel() a PresenceChannel instance. When calling this you just need to pass in the required channel name.

1reactivePusher.getChannel("some channel name")
2        .subscribe({ // do something with the channel })

Checking channel subscription state

You can also use the isChannelSubscribed(), isPrivateChannelSubscribed()and isPresenceChannelSubscribed()methods to check the subscription state of a channel.
When calling this method you need to pass in the desired channel name which you wish to check. Upon a successful request you will be returned a boolean value if the channel is subscribed to.

1reactivePusher.isChannelSubscribed("some channel name")
2        .subscribe({
3            // do something with the channel subscription result
4        })

Subscribing to channels

Using the subscribeToChannel(), subscribeToPrivateChannel() or bindToPrivateChannelEvent() methods allow you to subscribe to events from a given channel.
When subscribing to a channel you need to pass the channel name along with a collection of events that you wish to subscribe to, when binding you only need to pass a single event that you wish to bind to. When an event is received you will get an instance of a ChannelEvent from the callback.

1reactivePusher.subscribeToChannel("some channel name", events...)
2        .subscribe({ // do something with the channel event })

Triggering events

You can also trigger events from this library by using the triggerEvent() method. This just requires you to pass in the channel name, the event name and the data you wish to pass with that event.

1reactivePusher.triggerEvent("some channel name", "some event name", "some data")
2        .subscribe({ // do something with the trigger completion })

Conclusion

Using Pusher in our Android app has allowed us to introduced a more pro-active experience for our users, ensuring their always shown fresh content regardless of where the data was changed or who by. Creating Reactive Pusher has also allowed us to implement it in a way that fits in well with our architecture. Both of these together means not only our users our happier, but also our developers!
Are you using Pusher in your apps, thinking of doing so or even have questions about Reactive Pusher? Feel free to drop us a response or get in touch over on Twitter.


❤️ Kotlin? Fill in our State of Kotlin survey to help us discover how developers like you are picking up Kotlin, and get a chance to win a trip to KotlinConf in Amsterdam! Participate here.