Firebase Cloud Messaging in Android

What is FCM?

It is a cross-platform messaging solution that reliably delivers messages at no cost. It can send notifications that are displayed to your user or data messages which can be consumed by application code.  Messages can be distributed to client app in any of three ways:

  1. To Single devices,
  2. To groups of devices,
  3. To devices subscribed to topics (news, games etc.).

It can send acknowledgments from devices back to your server over FCM’s reliable and battery-efficient connection channel. It is highly efficient: it is claimed that 95% of the messages delivered in fewer than 250 milliseconds.

At the end of the post you can find the guideline about how to move from GCM to FCM

How does it work?

An FCM implementation includes a client app, an app server that interacts with FCM via HTTP or XMPP protocol and a Notification Console which can be used as an alternate to app server to send the notifications and messages. You can compose and send messages using the app server as well as with the Notifications console.

android1

Figure 1:  A basic app-server-console communication

Message Structure:

You can send two types of messages to client app:

  1. Notification messages.
  2. Data messages, which are consumed by the client app.

Notification messages

Here is a JSON-formatted notification message.

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

 

The notification key in the data bundle contains the notification. Above mentioned notification will be delivered to the device whose registration token (we will cover later about the device token) is mentioned with the key “to”. A Notification message is delivered to the notification tray when the app is in the background. For apps in the foreground, notification messages are handled by call-back’s onMessageReceived() .

 

 

Data messages

Data messages can have a 4KB maximum payload.

Here is a JSON-formatted message:

{
“to”: “bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1…”,
“data”: {
“Nick”: “Mario”,
“body”: “great match!”,
“Room”: “PortugalVSDenmark”
},
}

Client app receives a data message in onMessageReceived() irrespective of the fact whether app is in foreground or background.

 

Messages with both notification and data payloads

  • 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 message object with both payloads available. Notification tray doesn’t receive anything in this case

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

{
“to” : “APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx…”,
“notification” : {
“body” : “great match!”,
“title” : “Portugal vs. Denmark”,
“icon” : “myicon”
},
“data” : {
“Nick” : “Mario”,
“Room” : “PortugalVSDenmark”
}
}   Set up FCM with android app:1.      gradle dependency :      compile ‘com.google.firebase:firebase-messaging:x.x.x’2.      A service that extends FirebaseInstanceIdService is required to handle the creation, rotation, and updating of registration tokens which is required to send the notification or message to specific devices or for creating device groups.          <manifest>
<application><service
android:name=”. MyFirebaseInstanceIDService”>
<intent-filter>
<action android:name=”com.google.firebase.INSTANCE_ID_EVENT”/>
</intent-filter>
</service>
</application>

</manifest>

Public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService             {@Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
sendRegistrationToServer(refreshedToken);
}}

 

Send the message from Firebase Console

1.       Open the Notifications tab of the Firebase console and select New Message.

2.       Enter the message text.

3.       Select Single Device for the message target.

4.       In the field labelled FCM Registration Token, enter the registration token you obtained in a previous section of this guide.

android2

Figure 2:  console page for sending a message

Receive messages: 1.      A service that extends FirebaseMessagingService is required if you want to do any message handling beyond receiving notifications on apps in the background. By overriding the method onMessageReceived, you can perform actions based on the received Remote Message object and get the message data:             Public class MyFirebaseMessagingService extends FirebaseMessagingService               {@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, “From: ” + remoteMessage.getFrom());

Log.d(TAG, “Message data payload: ” + remoteMessage.getData());

Log.d(TAG, “Message Notification Body: ” +                                remoteMessage.getNotification().getBody());

}

}

2.      To receive notifications in foregrounded apps, to receive data payload, to send upstream messages, and so on, you must extend this service.

 <manifest> 
            <application>

<service
     android:name=".MyFirebaseMessagingService">
     <intent-filter>
         <action android:name="com.google.firebase.MESSAGING_EVENT"/>
     </intent-filter>
 </service>    </application>
 
 </manifest>

Topic Messaging:

Based on the publish/subscribe model, FCM topic messaging allows you to send a message to multiple devices that have opted for particular topic. FCM handles routing and delivering the message reliably to the right devices.

For example, Users of a sports app could subscribe to automatic updates in live game scores for their favourite teams.

Subscribe the client app to a topic 

When a client app subscribes to a new topic name (one that does not already exist for your Firebase project), a new topic of that name is created in FCM and any client can subsequently subscribe to it.

FirebaseMessaging.getInstance().subscribeToTopic("news");

Both the server and FCM console can be used to send a topic message.Following is the sample post request from server for topic messageHTTP POST request Send to a single topic:

URL: https://fcm.googleapis.com/fcm/send

Send to a Single topic:

{
                       "to": "/topics/Golf",
                       "data": {
                                       "message": “Woods made history!!"
                       }
        }

Send to a multiple topic:

{
        "condition": "‘Golf' in topics || ‘Cricket' in topics",         "data": {
                       "message": “Subsidy worth $7mn allotted!"
        }
 }

HTTP response:  We receive response for above mentioned post requests depending upon whether delivery is successful or failed. Following are the json format for the successful or failed delivery response.

//Success example:
 {
                "message_id": "1023456"
 }  //Failure example:
 {
                "error": "TopicMessagerateExceeded"
 }

Device group messaging:With device group messaging, app servers can send a single message to multiple instances of an app running on devices belonging to a group. Typically, “group” refers a set of different devices that belong to a single user. All devices in a group share a common notification_key, which is the token that FCM uses to deliver messages to all devices in that particular group. Before sending messages to a device group, you must:

1. Obtain registration tokens for each device you want to add to the group.

2. Create the notification_key, which identifies the device group by mapping a particular group (typically a user) to all of the group’s associated registration tokens.

3. To create a device group, send a POST request that provides a name for the group,   and a list of registration tokens for the devices. FCM returns a  new notification_key  that represents the device group

HTTP post to: https://android.googleapis.com/gcm/notification

Content-Type:application/json
 Authorization:key=API_KEY
 project_id:SENDER_ID
 
 {
        "operation": "create",
        "notification_key_name": "appUser-Chris",
        "registration_ids": [”token1", ”token2", ….. ”token 9"]
 }

Response :

{
        "notification_key": "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ“

}

·         Now send the message to the group of device, which is similar to sending the message to a single device.

{
                       "to": “APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ ",
                       "data": {
                                       "hello": “Woods made history!!"
                       }
        }

A simple use case for the device group messaging could be making a group on social networking apps. A  social networking app now a days is supposed to have the features like  enable group messaging (sending a same message to a few selected users ) or making a group of some people who are using the same app, which is a common feature  in many chatting app now a days. Such a scenario is perfect use case for device group messaging.

The difference between topic messaging and device group messaging is that in topic messaging all the devices who have subscribed to a particular topic were able to receive the messages for that topic, while on the other hand with device group messaging we can form a group of few devices based upon their registration  token. These devices receive all the messages which server or FCM console is intended to send to this particular group.

Send an upstream message:

If your app server implements the XMPP Connection Server protocol, it can receive upstream messages from a user’s device to the cloud.

FCM can solve a lot of use cases like:

·         Send acknowledgments from your device to other device via server or your device to server,

·         Send chat messages from your device to other device via server, or other messages from your devices back to your server.

To perform all the above mentioned functionalities we must have to send the message to server, which is called upstream messaging.

Device to device messaging is not supported by FCM. Upstream messages are designed to go from your mobile device to your XMPP app server. Once your XMPP app server receives the message it can decide to send a downstream message to a mobile device.

To initiate an upstream message, the client app sends a request containing the following:

•      The address of the receiving app server in the format SENDER_ID@gcm.googleapis.com.

•      A message ID that should be unique for each sender ID

•      The message data comprising the key-value pairs of the message’s payload.

FirebaseMessaging fm = FirebaseMessaging.getInstance();
 fm.send(new RemoteMessage.Builder(SENDER_ID + "@gcm.googleapis.com")
    .setMessageId(Integer.toString(msgId.incrementAndGet()))
    .addData("my_message", "Hello World")
    .addData("my_action","SAY_HELLO")
    .build());

•      Check the status of upstream messages with callbacks onMessageSent and onSendError.

SENDER_ID is a unique numerical value, which is created when you create your Firebase project, available in the Cloud Messaging tab of the Firebase console Settings pane. The sender ID is used to identify each app server that can send messages to the client app.

Receive XMPP messages on the app server:

When FCM receives an upstream messaging call from a client app, it generates the necessary XMPP stanza for sending the upstream message. FCM adds the category and from fields, and then sends a stanza like the following to the app server:

<message id="">                 

<gcm xmlns="google:mobile:data">                             

    {                                 

   "category":"com.example.yourapp", // to know which app sent it                                 "data":                               

  {                                  "hello":"world",                               

 },                                

"message_id":"m-123",                                

"from":"REGID"                              

 }               

  </gcm>           

    </message>

Sending an ACK message:

In response to an upstream message like the above, the app server must use the same connection to send an ACK message containing the unique message ID. If FCM does not receive an ACK, it may retry sending the message to the app server.

               <message id="">       

         <gcm xmlns="google:mobile:data">                     

           {                                  

 "to":"REGID",                                 

 "message_id":"m-123"                                 

"message_type":"ack"                              

   }              

   </gcm>              

 </message>

Migration from GCM to FCM:

Google will continue to support the current version of GCM Android and because a lot of developers are using GCM SDKs today to handle notifications, and client app upgrade takes time.

But all new client-side features will be added to FCM SDKs only moving forward. You are strongly encouraged to upgrade to FCM SDKs.

To upgrade from GCM SDKs to FCM SDKs, please see the following guildelines.

•      In the Firebase console, select Import Google Project.

•      All the permissions required by FCM are now added automatically by library. Hence the following permissions required by GCM are no longer need to be mentioned. These should be removed.

<manifest>

   <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” />

</manifest>

•       com.google.android.gms.gcm.GcmReceiver is already with FCM added automatically.  Hence Following GcmReceiver   not needed to add in manifest.These should be removed.

<manifest>
<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>

 

    </application>
</manifest>

  • A service extendingInstanceIDListenerService in GCM is replaced with FirebaseInstanceIdService and it required only if you want to access the FCM token.

This is needed if you want to

  1. Manage device tokens to send a messages to single device directly, or
  2. Send messages to device group, or
  3. Subscribe devices to topics with the server.

If you don’t use these features, you can completely remove this service along with client code to initiate the generation of a registration token.

AndroidManifest.xml before

<manifest>
<application><service
android:name=”.MyInstanceIDListenerService”
android:exported=”false”>
<intent-filter>
<action android:name=”com.google.android.gms.iid.InstanceID” />
</intent-filter>
</service>

</application>

</manifest>

AndroidManifest.xml now

<manifest>
<application>
<service
android:name=”.MyInstanceIDListenerService”>
<intent-filter>
<action android:name=”com.google.firebase.INSTANCE_ID_EVENT” />
</intent-filter>
</service>

</application>

</manifest>

·         Following Registration which required earlier is no longer needed to be mentioned explicitly it should be removed in case of FCM.

InstanceID instanceID = InstanceID.getInstance(this);


String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// [END get_token]
Log.i(TAG, “GCM Registration Token: ” + token);

·         GcmListenerService is replaced with FirebaseMessagingService and it now required only for the following use cases:

  1. Receiving messages with notification payload while the application is in foreground
  2. Receiving messages with data payload only
  3. Receiving errors in case of upstream message failures.

If you don’t use these features, and you only care about displaying notifications messages when the app is not in the foreground, you can completely remove this service.

Change MyGcmListenerService to extend FirebaseMessagingService and update the signature of the method onMessageReceived ()

MyGcmListenerService.java before

public class MyGcmListenerService extends GcmListenerService {
@Override
public void onMessageReceived(String from, Bundle data){

}

}

MyGcmListenerService.java after

public class MyFcmListenerService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage message){
String from = message.getFrom();
Map data = message.getData();
}
}

•      GcmPubSub methods have been integrated and simplified in the FirebaseMessaging class.


 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s