+1

Google Cloud Messaging - GCM

If you have little to no knowledge on Google Cloud Messaging (or shortly GCM), then this article is just for you. Here I've tried to give an elaborate description of what GCM is, it's overview, components, how it works, it's pros and cons, and finally a brief tutorial on how to implement it in your project. Note that this article is intended for the client-side developers, not the server-side. To be more precise, it's been written from the android development perspective. That being said, anyone with a thirst for knowledge may find this post helpful. Anyway, before diving deeper into the write-up, let's have a quick look at what GCM is.

Google Cloud Messaging (GCM) is a free service that enables developers to send messages between servers and client apps. This includes downstream messages from servers to client apps, and upstream messages from client apps to servers. If you intend to transfer not more than 4kb of payload to your client app, i.e. - an instant messenger, then you’re good to go with the GCM service. The GCM service handles all aspects of queuing of messages and delivery to and from the target client app. So you have a very few things to worry about for these purposes.

Architectural Overview

A GCM implementation includes a Google connection server, an app server in your environment that interacts with the connection server via HTTP or XMPP protocol, and a client app.

architectural overview.png

Figure 1. GCM Architecture.

Here's how these components interact:

Google GCM Connection Servers accept downstream messages from your app server and send them to a client app. The XMPP connection server can also accept messages sent upstream from the client app and forward them to your app server. For more information, see About GCM Connection Server.

On your App Server, you implement the HTTP and/or XMPP protocol to communicate with the GCM connection server(s). App servers send downstream messages to a GCM connection server; the connection server queues and stores the message, and then sends it to the client app. If you implement XMPP, your app server can receive messages sent from the client app. If you're not a server- side engineer, you don't have to worry much about it.

The Client App is a GCM-enabled client app. To receive and send GCM messages, this app must register with GCM and get a unique identifier called a registration token. We’re closing in to this topic very soon. So keep calm and go on reading.

Key Concepts

These are the key terms and concepts involved in GCM. Take it to your heart as much as possible, because you have to deal with them over and over again while working with GCM. GCM is broadly divided into these categories:

Components — The entities that play a primary role in GCM.

Credentials — The IDs and tokens that are used in GCM to ensure that all parties have been authenticated, and that the message is going to the correct place.

You're not supposed to have fully understood these concepts just yet and you don’t know the gravity of these things, but you’ll get to that in a moment. So keep patience and have a look at this table.

Selection_047.png

Lifecycle Flow

1. Register to enable GCM: An instance of a client app registers to receive messages. For more discussion, see Registering Client Apps.

2. Send and receive downstream messages.

  • Send a message: The app server sends messages to the client app:
  1. The app server sends a message to GCM connection servers.
  2. The GCM connection server enqueues and stores the message if the device is offline.
  3. When the device is online, the GCM connection server sends the message to the device.
  4. On the device, the client app receives the message according to the platform-specific implementation. See your platform-specific documentation for details.
  • Receive a message: A client app receives a message from a GCM connection server. See your platform-specific documentation for details on how a client app in that environment processes the messages it receives.

3. Send and receive upstream messages: This feature is only available if you're using the XMPP connection server.

  • Send a message: A client app sends messages to the app server:
  1. On the device, the client app sends messages to the XMPP connection server. See your platform-specific documentation for details on how a client app can send a message via XMPP.
  2. The XMPP connection server enqueues and stores the message if the server is disconnected.
  3. When the app server is re-connected, the XMPP connection server sends the message to the app server.

Receive a message: An app server receives a message from the XMPP connection server and then does the following:

  1. Parses the message header to verify client app sender information.
  2. Sends "ack" to the XMPP connection server to acknowledge receiving the message.
  3. Optionally parses the message payload, as defined by the client app.

Enough of this Lifecycle flow, let's now have a closer look at the messages that the app server sends to GCM to hand them over to the client apps, especially what they contain, what there components are and what options they can provide.

Messaging Concepts and Options

Google Cloud Messaging APIs offer a broad range of messaging options and capabilities. To make the extent of this article as concise as possible, I’m only describing the fundamental components of GCM messages and listing some of the most commonly used message options. If you’re a developer on the native side, this should not be much of a concern for you. But still you should have an overview of what a message can contain to get a clear understanding since you'll need this knowledge while implementing GCM in your client app.

Components of a message

The app server builds a downstream message request from these fundamental components: the target, the message options, and the payload. These components are common between the GCM HTTP and XMPP connection server protocols.

Target

When your app server sends a message, it must specify a target that identifies the destination of the message. Specify the target using the field to. This field can contain a single registration token, a topic, or a notification key (for sending to a device group).

Options

The app server can set various options when sending a downstream message to a client app, such as whether that message should be replaced by a subsequent one. For the full list of message options, see the reference information for your chosen connection server protocol, HTTP or XMPP. Some commonly used message options have been described below.

Payload

For downstream messaging, GCM provides two types of payload: notification and data. Notification is the more lightweight option, with a 2KB limit and a predefined set of user-visible keys. Data payload lets developers send up to 4KB of custom key/value pairs. Notification messages can contain an optional data payload which is delivered when users click on the notification.

Selection_048.png

Use notifications when you want GCM to handle displaying a notification on your client app’s behalf. Use data messages when you want your app to handle the display or process the messages on your Android client app.

The app server can send a message including both notification and data payloads. In such cases, GCM handles displaying the notification payload and the client app handles the data payload. For more information and examples, see Notifications and data in the message payload

Commonly used message options

Non-collapsible messages

A non-collapsible message implies that each individual message is delivered to the device. A non-collapsible message delivers some useful content. Messages are non-collapsible by default except for notification messages, which are always collapsible. GCM does not guarantee the order of delivery.

Some typical use cases of non-collapsible message are chat messages or critical messages. For example, in an IM application, you would want to deliver every message, because every message has different content.

Note: There is a limit of 100 messages that can be stored without collapsing. If the limit is reached, all stored messages are discarded. When the device is back online, it receives a special message indicating that the limit was reached. The application can then handle the situation properly, typically by requesting a full sync from the app server.

Collapsible messages

A collapsible message is a message that may be replaced by a new message containing the same collapse key if it has yet to be delivered to the device.

Two common use cases of collapsible message are Send-to-Sync messages and notifications. A Send-to-Sync message is a "ping" that tells a mobile application to sync data from the server. An example would be a sports application that updates users with the latest score. Only the most recent message is relevant.

GCM allows a maximum of 4 different collapse keys to be used by the app server per device at any given time. In other words, the GCM connection server can simultaneously store 4 different collapsible send-to-sync messages per device, each with a different collapse key. If you exceed this number GCM will only keep 4 collapse keys, with no guarantees about which ones are kept. Too bad, GCM!

Notifications and data in the message payload

Notifications provide an easy way for developers to send a user-visible display message with some predefined keys and optional custom key/value pairs. Data payloads include the developer’s custom key/value pairs only, and the client app must handle the message. You can send messages that have both notification and data payloads.

Notifications

To send notifications set notification with the necessary predefined set of key options for the user-visible part of the notification message. For example, here is a JSON-formatted notification message in an IM application. The user can expect to see a message with the title "Portugal vs. Denmark" and text "great match!" on the device:

{
  "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
  "notification" : {
   "body" : "great match!",
   "title" : "Portugal vs. Denmark",
   "icon" : "myicon"
  }
}

Notifications are delivered to the notification tray when the app is inactive. For an active Android app, notification payloads are passed to onMessageReceived() under the notification key in the data bundle.

Data messages

Set data with your custom key/value pairs to send a data payload to the client app. Data messages can have a maximum 4KB payload.

For example, here is a JSON-formatted message in the same IM application as above, where the information is encapsulated in data and the client app is expected to interpret the content:

{
   "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
   "data" : {
     "Nick" : "Mario",
     "body" : "great match!",
     "Room" : "PortugalVSDenmark"
   },
 }

On Android, a client app receives data messages in onMessageReceived() and can handle the key/value pairs accordingly.

Hybrid messages with both notification and data payload

App behavior when receiving messages that include both notification and data payloads depends on whether the app is in the background, or the foreground — essentially, whether or not it is active at the time of receipt.

  • When in the background, apps receive the notification payload in the notification tray, and only handle the data payload when the user taps on the notification.

  • When in the foreground, your app receives a bundle with both payloads available.

Here is a JSON-formatted message can containing both notification and data:

{
    "to" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
    "notification" : {
      "body" : "great match!",
      "title" : "Portugal vs. Denmark",
      "icon" : "myicon"
    },
    "data" : {
      "Nick" : "Mario",
      "Room" : "PortugalVSDenmark"
    }
}

Which one to use?

Collapsible messages are a better choice from a performance standpoint, provided your application doesn't need to use non-collapsible messages. However, if you use collapsible messages, remember that GCM only allows a maximum of 4 different collapse keys to be used by the GCM connection server per registration token at any given time. You must not exceed this number, or it could cause unpredictable consequences.

Setting the priority of a message

You have two options for assigning delivery priority to downstream messages: normal and high priority. Delivery of high and normal priority messages works like this:

  • High priority: GCM attempts to deliver high priority messages immediately, allowing the GCM service to wake a sleeping device when possible and open a network connection to your app server. Apps with instant messaging, chat, or voice call alerts, for example, generally need to open a network connection and make sure GCM delivers the message to the device without delay. Set high priority only if the message is time-critical and requires the user’s immediate interaction, and beware that setting your messages to high priority contributes more to battery drain compared to normal priority messages.
  • Normal priority: This is the default priority for message delivery. Normal priority messages won't open network connections on a sleeping device, and their delivery may be delayed to conserve battery. For less time-sensitive messages, such as notifications of new email or other data to sync, choose normal delivery priority.

Valid values are high and normal. For more details, see the server reference for HTTP or XMPP.

Receiving messages from multi-senders

GCM allows multiple parties to send messages to the same client app. For example, suppose the client app is an articles aggregator with multiple contributors, and each of them should be able to send a message when they publish a new article. This message might contain a URL so that the client app can download the article. Instead of having to centralize all sending activity in one location, GCM gives you the ability to let each of these contributors send its own messages.

To make this possible, make sure each sender generates its own sender ID. See the client documentation for your platform for information on on how to obtain the GCM sender ID. When requesting registration, the client app fetches the token multiple times, each time with a different sender ID in audience field.

Finally, share the registration token with the corresponding app servers (to complete the GCM registration client/server handshake), and they'll be able to send messages to the client app using their own authentication keys.

Note that there is limit of 100 multiple senders.

Lifetime of a message

When an app server posts a message to GCM and receives a message ID back, it does not mean that the message was already delivered to the device. Rather, it means that it was accepted for delivery. What happens to the message after it is accepted depends on many factors.

In the best-case scenario, if the device is connected to GCM, the screen is on and there are no throttling restrictions, the message will be delivered right away.

If the device is connected but idle, the message will still be delivered right away unless the delay_while_idle flag is set to true. Otherwise, it will be stored in the GCM connection servers until the device is awake. And that's where the collapse_key flag plays a role: if there is already a message with the same collapse key (and registration token) stored and waiting for delivery, the old message will be discarded and the new message will take its place (that is, the old message will be collapsed by the new one). However, if the collapse key is not set, both the new and old messages are stored for future delivery.

If the device is not connected to GCM, the message will be stored until a connection is established (again respecting the collapse key rules). When a connection is established, GCM will deliver all pending messages to the device, regardless of the delay_while_idle lag. If the device never gets connected again (for instance, if it was factory reset), the message will eventually time out and be discarded from GCM storage. The default timeout is 4 weeks, unless the time_to_live flag is set.

Finally, when GCM attempts to deliver a message to the device and the application was uninstalled, GCM will discard that message right away and invalidate the registration token. Future attempts to send a message to that device will result in a NotRegistered error.

Why you should use GCM?

If you've carefully read everything written so far, you'll get the idea why GCM should be preferred for cloud to device messaging. GCM provides the interface with all the features needed to send messages from app server to client app and vice versa. It serves the functions of queuing up messages and scheduling for delivery which has feature like exponential back-off for improving battery life of the device. It is very easy to implement both at the server and native side. In a nutshell, GCM for cloud to device messaging is the best option an developer can have for his/her app.

Code Examples

Time for a brief tutorial on GCM implementation. This page covers a detailed procedure on how to work with GCM in you android app allowing you to download and use their demo. I'm just getting the gist out of it for you.

Download the demo or Create your own project

Download the demo app provided in the link above and open it in the Android Studio. Or you can create a whole new android project following the standard procedure. If you already have a project, jump to next.

Get a configuration file

You need to provide some information to get a configuration file and set up your project. Use the package name of your project and register it here. Don't forget to modify the app name. Copy and save the Server API key and Sender ID for future use.

After you complete the registration, download the google-services.json file to add to your project.

Before running the app, you'll need to add the saved Server API key as the value of API_KEY in GcmSender.java. GcmSender.java is used to mimic an app server to push message or notification to you client app. We will get to that point very soon.

Run the sample

If you've download the sample demo app and want to try it from Android Studio, this portion is just for you. Otherwise directly jump to the next heading.

First, make sure your API key is provided as the value of API_KEY in GcmSender.java.

Inside Android Studio, click the run button and select an attached device.

When the sample application loads on your device, run the following gradle command to send a notification to all registered app instances:

Linux/Mac:

./gradlew run -Pmsg="<message>"

Windows:

.\gradlew.bat run -Pmsg="<message>"

Set Up Google Play Services

To write your client application, use the GoogleCloudMessaging API. To use this API, you must set up your project to use the Google Play services SDK, as described in Set up Google Play Services SDK.

If you're using Android Studio, this is the string to add to the dependency section of your application's build.gradle file:

dependencies {
  compile "com.google.android.gms:play-services-gcm:8.4.0"
}

Add the configuration file to your project

Copy the google-services.json file you just downloaded into the app/ or mobile/ directory of your Android Studio project.

Create the necessary classes

All you have to do now is create the necessary classes. One is to register your client app to GCM using the Sender ID of the app server and subscribe to the topic(s). Another class is for the listening to message/notification retrieved from GCM server.

1. Class for app registration and subscription: For each device, first get a registration token for Google Cloud Messaging from the InstanceID API. This token is used to identify the instance of the app running on your device. Retrieve the token using an IntentService as shown in the code snippet below. Because this may result in a network connection, make sure it is executed off the UI thread. OK, just create this class in you project's java directory.

public class RegistrationIntentService extends IntentService {
    // ...
    @Override
    public void onHandleIntent(Intent intent) {
        // ...
        InstanceID instanceID = InstanceID.getInstance(this);
        String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
                GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
        //congratulations! registration complete!
        // ...
    }
    // ...
}

The sample's demo server (GcmSender.java) sends your message string to the topic /topics/global by default. To receive messages sent to this topic, each app instance subscribes to this topic:

private void subscribeTopics(String token) throws IOException {
      GcmPubSub pubSub = GcmPubSub.getInstance(this);
      for (String topic : TOPICS) {
      // TOPICS is just a string array holding all the topics which the app server pushes
      // notifications on
          pubSub.subscribe(token, "/topics/" + topic, null); //subscription complete!
      }
}

Your app server may send messages to other topics. So you have to know them beforehand and list them in the TOPICS array.

2. Class for listening to GCM messages/notifications: Create another service that extends GcmListenerService to handle messages captured by GcmReceiver. GcmReceiver extends WakefulBroadcastReceiver, guaranteeing that the CPU is awake so that your listener service can complete its task.

public class MyGcmListenerService extends GcmListenerService {
/*you can change the class name to whatever you want*/
    // ...
     @Override
    public void onMessageReceived(String from, Bundle data) {
        String message = data.getString("message");
        Log.d(TAG, "From: " + from);
        Log.d(TAG, "Message: " + message);
        if (from.startsWith("/topics/")) {
            // message received from some topic.
	  // you can filter further like “/topics/topic1” if the app server sends so
        } else {
            // normal downstream message.
        }
        // ...
    }

You need to add these permissions to your AndroidManifest.xml file:

<permission android:name="<your-package-name>.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
  <uses-permission android:name="<your-package-name>.permission.C2D_MESSAGE" />

Since we're using two services and GcmReceiver, you have to add them too. The basic structure of your AndroidManifes.xml file should look like this:

<manifest package="com.example.gcm" ...>
    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <permission android:name="<your-package-name>.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="<your-package-name>.permission.C2D_MESSAGE" />
    <application ...>
        <receiver
            android:name="com.google.android.gms.gcm.GcmReceiver"
            android:exported="true"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="com.example.gcm" />
            </intent-filter>
        </receiver>
        <service
            android:name="com.example.MyGcmListenerService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
        <service
            android:name="com.example.MyInstanceIDListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.gms.iid.InstanceID" />
            </intent-filter>
        </service>
    </application>
</manifest>

Up to now, I've just discussed the fundamental concepts of GCM along with some sample codes and basic tutorial for setting up GCM in an android project. But, truth be told, this is too huge a topic to cover in a single post. As of now, this should be all you need to make a start. For more information on GCM and a complete guide and reference visit the android developers site. Happy coding.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí