I recently outlined the benefits behind building Android apps with PubNub and how to implement realtime chat with Publish/Subscribe messaging. This has all been done with our sample Data Stream App for Android. In this blog, we dive deeper into Presence tracking, including how we made the “Presence” (buddy list/who’s online) tab in our sample app along with the associated patterns, data structures and resources in Android.
This will allow you to create a “buddy list” tab that can:
Display a dynamic list of users/devices with current presence state information.
Detect offline/disconnected users using the heartbeat feature.
Use the hereNow() feature to populate the presence list on first connection.
To do this, we dive into the core concepts of presence callbacks, more advanced Presence use cases, as well as the Android machinery that makes it all possible. The full Android Data Stream application that we’ve created takes advantage of several PubNub features: Publish/Subscribe messaging, Presence tracking, and Multiplexing & Channel Groups. The app uses Publish/Subscribe messaging for a realtime chat feature, Presence tracking for a buddy list, and Multiplexing to show the last messages across a group of channels using a single connection. In this case, our Login activity is just a dummy one to collect the username.
Here’s the animated GIF of the full app experience:
For the purposes of this article, we’ll just be looking at the Presence tab of the application – more specifically, the buddy list feature. That’s this part:
What exactly is Presence? Presence tracking is a tremendously useful feature for applications in gaming, social, collaboration, ride-hailing and IoT spaces. If you’re familiar with chat applications, you’ve seen a friendly “buddy list” before. PubNub generalizes this concept across all the use cases of Publish/Subscribe messaging, making it easy to see which devices are online and connected to channels using its Presence API. This makes it easy to see which users are online for a chat application, which cars are available in the case of a ride-hailing app, or which sensors are currently transmitting or receiving in the case of an IoT app. As devices come online, leave or become disconnected from the network, PubNub sends the corresponding Presence events of join, leave and timeout to your application, allowing it to respond instantly to dynamic channel membership.
The PubNub Presence Add-On
Presence has a few nuances we should talk about before diving in further. The first is that Presence is an add-on, so you’ll need to make sure it’s enabled in your application configuration.
There are 3 types of presence events:
Join: a user/device has subscribed to the channel.
Leave: a user/device has disconnected or unsubscribed from a channel.
Timeout: the user/device has not responded to PubNub within the specified heartbeat configuration.
In our case, we simply pass through the string value of the event for display purposes. In your application, you might choose to use terms like online, offline, idle, disconnected, unknown, etc.
Advanced PubNub users might say “Hey, you forgot about the state-change event!” That’s a great catch! We’re not forgetting it, we’re just saving it as an advanced feature at the end so that we can focus on core presence features. Adding a few lines of code to handle that one additional event type in the future should be a piece of cake.
Initializing PubNub
If you want to use PubNub, you’ll need to make friends with the PubNub class. You can create several instances of the PubNub objects if you need multiple connections with different keys, callbacks or other configuration. In the sample app, we create two PubNub objects because we want to separate the Pub/Sub and Presence listeners from the Multiplexing feature. That’s a rare case – you’ll probably only need one instance.
In our case, we initialize the PubNub object(s) in the MainActivity class right after the user logs in via the LoginActivity. This is because the username is required as the UUID field of the configuration object below. Note: if your application allows rapid switching between users, you may want to think carefully about how to create and destroy the PubNub objects accordingly.
Just to recap the code above.
Step 1: create a PNConfiguration object.
Step 2: pass in the Publish and Subscribe keys.
Step 3: pass in the user/device UUID (whatever you like, as long as it’s unique – this UUID will also used by the presence API).
Step 4: set the secure option to use TLS.
Step 5: create a new PubNub instance using the specified PNConfiguration.
One thing we should also note – if you really want to dive into the fluent API, you can also do something like this:
Nice, now we’re having some fun with it!
Subscribing to a Channel
OK, now that we have a connection to PubNub, the next thing we’d like to do is subscribe to a channel so we can receive presence events and display them in the UI ListView. That takes three steps:
Step 1 (optional): Create an Adapter to bridge between the callback and the application UI View.
Step 2: Create a subscription callback (that calls methods in the adapter, if applicable).
Step 3: Perform the channel subscription.
Once the app completes the subscription, the callback will be invoked for each new presence event that comes from PubNub.
Here’s how to create a subscription callback:
In the code above, the status() method is where we handle events such as connection errors and reconnect events, publish or subscribe errors.
The message() method would be used if we also wanted this callback to handle message events on the channel. In this case, we omit them for code separation and instructional purposes.
In this case, the presence() method is where the magic happens – that’s where we put our logic for handling presence events. We get the username via the presence.getUuid() method, the event type using the presence.getEvent() method, and an ISO timestamp for display purposes. Once we have that Pojo object, we pass it along to the Adapter for processing and sending to the UI.
Here’s a recap of how to do the channel subscription: make sure to register the Presence callback as a listener on the PubNub object, and that the subscribe() call chain specifies the withPresence() option.
Let’s review that code a bit:
The first step is to register the callbacks with the PubNub object – you only need to do this once for each callback instance.
The next step is to subscribe to the desired channel, in this case, we want to make sure to specify withPresence()!
You only need to subscribe once (unless you subsequently call unsubscribe() on the channel and want to resubscribe).
That’s it! Now, as presence events come in from the Pub/Sub channel, the corresponding callback(s) will be invoked. Depending on your application, this might be all you need! Chances are though, you’ll need to propagate the data into your UI – that’s what we alluded to when we talked about the Adapter above. Let’s assume we’re using something like a ListView to display data, so we’ll want to check out how the PresenceListAdapter in the sample application works.
Understanding the Adapter
When would we need an adapter? Like we said before, the Adapter class is a way to bridge between our dynamic data collection and the Android UI. Here are the main aspects of the Adapter:
Part 1 (optional): a concrete Java collection containing the “real” values.
Part 2: mutation methods (in our case, just add()) to propagate new values from the PubNub callback using notifyDataSetChanged().
Part 3: a getView() method to instantiate each row of the View.
The reason why Part 1 is optional is that in some cases, the Java collection might be extremely large. It may not be possible nor desirable to keep all of those values in memory. Along those lines, it shouldn’t be hard to imagine a scenario where we omit the values List below, and use dynamic requests to a SQLite DB, file, or other data store in the getView() call accordingly.
There are a couple things we should mention as features & enhancements for the Buddy list:
We should only list users once.
We should only show the latest presence status.
We should move the latest updated users to the top.
To implement this, we keep a List in the Adapter to represent the users in order, and keep a LinkedHashMap<String, PresencePojo> to make it easy to update the data value corresponding to a given user.
We should note a couple other things about the add() method above. First, we’re prepending new elements to the values list using List.add(index, value) where index is 0. In other cases, you may just want to use addition at the end of the list using List.add(value). Secondly, we’re using a RowUi object to hold all the UI elements that we need to update for a given row. That is what the getTag() and setTag() calls on the convertView instance are all about.
Before we forget, here’s the RowUi object for the Presence feature – it’s very small.
One last thing to note on this topic is that updates to the UI need to happen on the UI thread; that’s why we keep a reference to the context in the Adapter, and make sure to run the notifyDataSetChanged() call on the UI thread. If you don’t do that, you’ll see a ton of warnings in the logs and/or crash the app.
Advanced Presence Features
Displaying a list of users/devices is awesome, but how do we initialize it when we first start the app? The PubNub API makes this easy by exposing a hereNow() function that lets you get the full list of members in a channel. We call that method right after we subscribe to the channel in the initChannels() method in MainActivity. Here’s how it looks:
The hereNow() method accepts a list of channels to check, as well as a callback to be invoked when the results return. Right now, we return immediately if the status.isError(); in your case, you might want to display an error to the user or send it off to an exception logging service. If the call is successful, we iterate over the PNHereNowChannelData objects in the PNHereNowResult object to get the corresponding user UUIDs, create new Pojo objects for them with status “join”, and forward those on to the PresenceAdapter for subsequent display in the Presence Tab ListView. Not too shabby!
Also note that if the channel() parameter is not specified, the method will return a list of all subscribers on every channel. Remember, with great power comes great responsibility!
Channel Subscriptions with whereNow()
Besides getting the list of a given channel’s subscribers, there’s another similar use case for getting the list of what channels a user/device (actually, UUID) are subscribed to. Although this isn’t demonstrated in the full tutorial app, here’s the sample code for how to do it:
One aspect of this feature worth mentioning is that if the uuid() parameter is not specified, the method will return a list of everyone’s channels everywhere. That could be a lot of data!
Extra Data Attributes with state()
There are a ton of cases where data streams make sense. In the case of chat messages, it is intuitive to send messages over the channel for each chat message sent by a user. But what about other data, such as the typing… indicator or metadata such as location or extended presence information (such as Available vs. DoNotDisturb)? PubNub’s state() methods make it easy to attach attributes to users (actually UUIDs) and update them via the presence channel, that is, a side channel outside the “normal” channel itself.
Here’s how we set these state attributes (for our own UUID):
When setState() calls occur, the new values are propagated using a state-change event type coming through the Presence event handler (just like join, leave or timeout events would be).
Alternatively, here’s how we get the state attributes (for any UUID) on-demand:
The current state may also be returned as part of the hereNow() call by using the .includeState(true) as follows:
Pretty nifty stuff!
Heartbeat Configuration & Timeouts
We alluded to this earlier – as devices encounter intermittent network connectivity, we may want to update their online status accordingly. To this end, PubNub offers two parameters to affect server-side detection of client outages: heartbeat and heartbeatInterval. The heartbeat timeout value reflects the amount of time that must pass without a heartbeat before a client is marked “timeout”. The heartbeatInterval value reflects the amount of time between client heartbeat pings to the server (by default, this is about half the heartbeat timeout value).
You can set these values in the PNConfiguration object as follows:
The default heartbeat interval for 60 seconds will be ((60/2) – 1) seconds, or 29 seconds.
If you’d like to use a custom heartbeat interval, you can do this instead:
In this case, the heartbeat timeout will be 60 seconds, and the client will send a heartbeat every 10s.
Wrapping Up
Thanks so much for stopping by and checking out our sample app for the Android SDK. Hopefully this gives you an idea of what’s possible! Let’s take a second to recap what we built:
A “buddy list” tab with dynamic list of users on a specified PubNub channel
The ability to detect leave and timeout events
Advanced state management for custom user/device attributes using PubNub state() methods
It shouldn’t be hard to imagine adapting this example to other use cases, such as online vehicles, systems or remote sensor station data!
In the next articles in the series, we’ll dive deeper into the topic of Multiplexing and Channel groups. Stay tuned, and please reach out anytime if you feel especially inspired or need any help!
The post Building Realtime Android Apps with PubNub’s Presence appeared first on PubNub.