GcmListenerService is not called When Application is in Background

Usman Kurd picture Usman Kurd · Feb 18, 2016 · Viewed 12.5k times · Source

GcmListenerService is not called when application is in background or when phone is locked or in sleep mode but notification is fired. How this will be called When App is in foreground its working ideally. Code for GcmListenerService is following

  public class MyGcmListenerService extends GcmListenerService {

    private static final String TAG = "MyGcmListenerService";
    LocalDataBaseManager mDbManager;
    String message;
    Random randomNumber;
    long ID;
    /**
     * Called when message is received.
     *
     * @param from SenderID of the sender.
     * @param data Data bundle containing message data as key/value pairs.
     *             For Set of keys use data.keySet().
     */
    // [START receive_message]
    @Override
    public void onMessageReceived(String from, Bundle data) {
        String message ;
        String title;
//        ID = Utils.getIDForPush("pushId",this);
//        if(ID == 0){
//            ID = 1;
//        }else {
//            ID += 1;
//        }
//        Utils.saveIDForPush("pushId",ID,this);
        Bundle bundle = data.getBundle("notification");
        if(bundle!= null){
        message = bundle.getString("body");
        title = bundle.getString("title");
            Log.d(TAG, "From: " + from);
            Log.d(TAG, "Message: " + message);}
        else {
            message ="";
            title = "NCMS";
        }

        mDbManager = LocalDataBaseManager.getInstance(this);
        if (from.startsWith("/topics/")) {
            Calendar c = Calendar.getInstance();
            SimpleDateFormat s = new SimpleDateFormat("ddMMyyyyhhmmss");
            String format = s.format(new Date());
            ID = Long.parseLong(format);
            String date = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.ENGLISH).format(new Date());
            Warnings warnings = new Warnings();
            warnings.setWARNING_ID(ID);
            warnings.setWARNING_EN(message);
            warnings.setWARNING_AR(message);
            warnings.setSTART_DATE_TIME(date);
            warnings.setNotification_type(String.valueOf(Constant.NotificationType.PUSH));
            warnings.setSEVERITY("");
            warnings.setEND_DATE_TIME("");
            warnings.setUPDATE_NO("");
            mDbManager.insertNotificationInfo(warnings);
            // message received from some topic.
        } else {
            // normal downstream message.
        }

        // [START_EXCLUDE]
        /**
         * Production applications would usually process the message here.
         * Eg: - Syncing with server.
         *     - Store message in local database.
         *     - Update UI.
         */

        /**
         * In some cases it may be useful to show a notification indicating to the user
         * that a message was received.
         */
//        KeyguardManager km = (KeyguardManager) this.getSystemService(Context.KEYGUARD_SERVICE);
//        boolean locked = km.inKeyguardRestrictedInputMode();
//
//        String release = android.os.Build.VERSION.RELEASE;
//
//
//        if (Integer.parseInt(String.valueOf(release.charAt(0))) < 5 && locked) {
//
//            this.stopService(new Intent(this, NotificationService.class));
//            Intent serviceIntent = new Intent(this, NotificationService.class);
//            this.startService(serviceIntent);
//
//        }
        sendNotification(title,message);
        // [END_EXCLUDE]
    }
    // [END receive_message]

    /**
     * Create and show a simple notification containing the received GCM message.
     *
     * @param message GCM message received.
     */
    private void sendNotification(String title,String message) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.putExtra("message",message);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);

        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ncms_launcher)
                .setContentTitle(title)
                .setContentText(message)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }
}

Manifest info for this service is following

 <service
        android:name=".gcm.MyGcmListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>

What I am missing here.

Answer

Adam Johns picture Adam Johns · May 6, 2016

It appears the heart of this issue is actually a server-side issue. If the server is sending notification messages, onMessageReceived won't be called if the app is in the background. The server should actually be sending data messages.

GCM Docs discuss the difference.

Basically, the payload of the message should have a data key such as

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

and NOT a notification key such as

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

More specifically, the GCM Docs state that messages sent including both data and notification payloads will be treated differently depending on if the app is in the foreground or background:

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.

This github thread also has a good explanation:

So there are two kinds of GCM messages:

  • Notification Messages - these are intended to generate a notification with no intermediate processing by the application. They only hit onMessageReceived if the app is running.

  • Data Messages - these are intended to silently pass data to the app's messaging service. They hit onMessageReceived even if the app is in the background. The service may then choose to generate a notification using the normal system notification APIs, or it may choose to handle the message silently.