Push messaging enables you to deliver timely information to your app. This capability is often the critical component to creating a dynamic and engaging app experience.
Since push notifications are often so important to a great app, it shouldn’t come as any suprise that keeping them working is a key concern for many developers.
For us as Pushbullet, push notification speed and reliability are mission critical for our Android app. Since they’re so important to our service, we’ve learned a lot about keeping them as reliable as possible. This post discusses some of the lessons we’ve learned over time that we wish we’d known earlier.
This post focuses on using Google Cloud Messaging to deliver push messages. If you’re thinking instead of polling for updates while your app is in the background, please abandon those thoughts now.
When your app is in the foreground, using standard web requests or sockets to get information is the right choice. This isn’t what push messaging is meant to replace. When your app is in the background though, don’t you dare poll for updates. This is exactly the type of bad behaviour that push messaging makes obsolete.
Why does it matter so much? Simple. Polling is worse by all measures—it’s harder on your servers, less timely, and affects your users’ battery life. There’s almost no better way to get someone to uninstall your app than to have them see it at the top of their battery usage screen.
A quick warning: this is an advanced post. If you’re looking for a tutorial on how to get GCM set up, check out Google’s documentation here.
Lesson #1: Prepare to see SERVICE_NOT_AVAILABLE a lot.
This fact is completely absent from Google’s GCM setup guide. That makes this the single most important thing to be aware of when registering for GCM in your Android app.
Here’s the deal: when you call register, it’s often going to fail with an IOException containing the message SERVICE_NOT_AVAILABLE.
This is likely to never happen on your test devices so, without knowing to expect it, you’ll probably not prepare for it. This will end up having a significant negative effect on your push messaging reliability because, once your app is in the wild, it’s going to happen a LOT.
How does one deal with this? That’s the easy part. This message means GCM wasn’t able to register so you’ll need to retry. The documentation for this error advises using exponential backoff and that’s fine advice.
Lesson #2: Be ready for register to repeatedly fail on some devices even though a working registration ID is created.
This tip is rather bizarre and may no longer be relevant but I have no way of confirming if the bug in GCM has been fixed so here it is.
The bug goes like this: no matter how many times you call register, it will always fail and throw an exception on some devices. Even though register is throwing an exception, a working registration ID is being created but not returned. To get this registration ID, add this permission to your GCM BroadcastReceiver’s IntentFilter:
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
Then you’ll be able to retrieve the working registration ID like so:
final String registrationId = intent.getStringExtra("registration_id");
As long as that string isn’t null or empty, you’ve got your working registration ID right there. Now it’s just a matter of saving it and reporting it to your server like you normally would.
The previous two tips help you to ensure that devices always register succesfully. The next two tips point out when you’ll need to re-register and how to do it most effectively. This is important keep push messaging functioning seamlessly.
Lesson #3: Be aware you’ll need to re-register every device each time you update your app.
This comes straight from the documentation but it’s easy to overlook so don’t forget to do it.
It’s unlikely that a user will open your app immediately after an update so you’ll want to kick off this re-registration automatically. This is critical to keeping push messaging working reliably after updates.
The best way to trigger the re-registering process automatically is to listen for the Intent Android broadcasts when your app has been updated. Here’s an example AndroidManifest entry of a BroadcastReceiver listening for this:
<receiver android:name=".UpdateReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:path="<YOUR_PACKAGE_NAME_HERE>" android:scheme="package" /> </intent-filter> </receiver>
When this BroadcastReceiver’s onReceive method is called, it’s your trigger to re-register with GCM. Forget your current registration ID, register for a new one, and then report it to your server.
Lesson #4: Be aware you’ll also need to re-register a device if the version of Android it’s running has been updated.
This isn’t documented anywhere (that I’m aware of) but I learned from a Google engineer who worked on GCM that the registration IDs are based on the Android ID of the device. Since the Android ID changes whenever the device’s OS is updated or a user does a factory reset, you’ll need to re-register the device whenever one of these things happens.
Again, just like after an app update, it’s extremely unlikely a user is going to open your app right away after updating their phone. You’ll need to trigger GCM re-registration automatically if you want push notifications to start working again without waiting for the user to open your app.
The best way to do this? Well, there’s only one way and it’s not exactly ideal. You’ll need to register your app to receive the Intent Android broadcasts whenever the device has been rebooted. To set this up, first you’ll need to add this permission to your Android Manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Then set up a BroadcastReceiver for this and add it to your Android Manifest like so:
<receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
Now, whenever this BroadcastReceiver’s onReceive method is called, you know it’s once again time to re-register.
You could be more efficient and only re-register if the device’s Android ID has changed but I’d rather be certain to get push messaging working again at the small cost of doing a little potentially unnecessary work. This is because I can’t be 100% sure the Android ID changing is a completely reliable tell about whether or not I need to re-register the device. Maybe someone who worked on this at Google could tell me if this is the case or not? :)
And that’s it. Those are my big 4 tips for GCM reliability.
I think it’s kind of unfortunate that keeping GCM working requires so much song-and-dance. It’s even worse that most of this is completely undocumented by Google. So it goes I guess. Hopefully this post will make more people aware of how to keep GCM humming nicely in their apps.