GcmListenerService не вызывается, когда приложение находится в фоновом режиме

GcmListenerService не вызывается, когда приложение находится в фоновом режиме или когда телефон заблокирован или находится в спящем режиме, но срабатывает уведомление. Как это будет называться. Когда приложение находится на переднем плане, оно работает идеально. Код для GcmListenerService следующий

  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());
    }
}

Информация о манифесте для этой службы приведена ниже.

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

Чего мне здесь не хватает.


person Usman Kurd    schedule 18.02.2016    source источник
comment
Если вы видите уведомление, это означает, что служба прослушивателя Gcm вызывается onMessageReceived ? Что вы имеете в виду под не звонили сюда?   -  person Much Overflow    schedule 18.02.2016
comment
Когда приложение находится на переднем плане, оно попадает в onMessageReceived, но приложение находится в фоновом режиме и не попадает в onMessageReceived.   -  person Usman Kurd    schedule 18.02.2016
comment
вы вызываете метод sendNotification из onMessageReceived. Вы говорите, что все еще видите уведомление, когда приложение находится в фоновом режиме, что означает, что onMessageReceived выполняется для отображения уведомления?   -  person Much Overflow    schedule 18.02.2016
comment
@MuchOverflow не обязательно верно, что onMessageReceived будет вызвано, если вы увидите уведомление. Это может зависеть от полезной нагрузки, которую сервер приложений отправляет в GCM. Проверьте мой ответ для деталей.   -  person Adam Johns    schedule 07.05.2016


Ответы (2)


Похоже, что суть этой проблемы на самом деле заключается в проблеме на стороне сервера. Если сервер отправляет уведомления, onMessageReceived не будет вызываться, если приложение находится в фоновом режиме. На самом деле сервер должен отправлять сообщения data.

документы GCM обсуждают разницу.

По сути, полезная нагрузка сообщения должна иметь ключ data, такой как

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

и НЕ ключ уведомления, такой как

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

В частности, в документах GCM указано, что отправленные сообщения, содержащие как полезные данные data, так и notification, будут обрабатываться по-разному в зависимости от того, находится ли приложение на переднем плане или в фоновом режиме:

Поведение приложения при получении сообщений, содержащих как уведомления, так и полезные данные, зависит от того, находится ли приложение в фоновом или на переднем плане — по сути, от того, активно оно или нет во время получения.

  • В фоновом режиме приложения получают полезные данные уведомлений в области уведомлений и обрабатывают полезные данные только тогда, когда пользователь нажимает на уведомление.
  • Находясь на переднем плане, ваше приложение получает пакет с обеими доступными полезными нагрузками.

В этой ветке github также есть хорошее объяснение:

Итак, есть два вида сообщений GCM:

  • Сообщения уведомлений — они предназначены для создания уведомлений без промежуточной обработки приложением. Они нажимают onMessageReceived только в том случае, если приложение запущено.

  • Сообщения данных — они предназначены для автоматической передачи данных в службу обмена сообщениями приложения. Они нажимают onMessageReceived, даже если приложение находится в фоновом режиме. Затем служба может выбрать создание уведомления с использованием обычных API-интерфейсов системных уведомлений или обработку сообщения без вывода сообщений.

person Adam Johns    schedule 06.05.2016
comment
Я не знал о полезной нагрузке уведомлений. Я всегда использовал данные для отправки уведомления в свое приложение. Спасибо, что указали :) - person Much Overflow; 07.05.2016
comment
такой комментарий наверняка заслуживает более 1 плюса - person Syed Muhammad Oan; 12.05.2016
comment
Просто отметим, что если вы добавите и то, и другое, Android будет рассматриваться как уведомление (пример) эноэ/8a611cac77fdea68c6a4b0d177177225 - person eOnOe; 13.08.2016
comment
омФФФФФг! вы спасли мой день, сэр. Чем вам очень! - person flagg327; 08.09.2016

Проблема, с которой я столкнулся, заключалась в том, что когда приложение находится в фоновом режиме или принудительно закрыто, тогда уведомление запускается, но не через GcmListenService, а через GCMReceiver. Поэтому я расширил GCMReceiver и сделал что-то вроде этого, когда приложение находится на переднем плане или в фоновом режиме или принудительно убито. он будет называться GCMListenerService после модификации следующим образом

 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());
    }
} 

и приемник GCM выглядит следующим образом

   public class GcmBroadcastReceiver extends GcmReceiver {

    LocalDataBaseManager mDbManager;
    @Override
    public void onReceive(Context context, Intent intent) {
        mDbManager = LocalDataBaseManager.getInstance(context);
        Bundle bundle = intent.getExtras();
        bundle.keySet();
        Set<String> keySet = bundle.keySet();
        if(keySet != null && keySet.isEmpty() == false) {
            Iterator<String> it = keySet.iterator();
           int i = 0;
            while(it.hasNext()){
               String  key = it.next();
               String  desc = bundle.getString(key);
               Log.d("BroadCast Values",key +"  "+desc);
            }
        }
        Log.d("", "In Receive Method of Broadcast Receiver");

        if (bundle != null && bundle.containsKey("gcm.notification.body")) {
            String message = bundle.getString("gcm.notification.body","");
            Long ID = new Date().getTime();
            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.
        }

        super.onReceive(context, intent);
//        ComponentName cn = new ComponentName(context.getPackageName(), RegistrationIntentService.class.getName());
//        startWakefulService(context, intent.setComponent(cn));
//        setResultCode(Activity.RESULT_OK);
    }
}

Ниже приведены изменения манифеста для GCMReceiver.

   <receiver
            android:name=".gcm.GcmBroadcastReceiver"
            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.uae.ncms" />
            </intent-filter>
        </receiver>
<service
        android:name=".gcm.MyGcmListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>
person Usman Kurd    schedule 21.02.2016
comment
Это неправильный ответ. Адам Джонс объясняет, как обрабатывать уведомления в фоновом режиме (или приложение закрыто). Это просто вопрос JSON. - person Francesco; 04.08.2016
comment
Подводя итог: чтобы всегда обрабатывать сообщение GCM в GCMListenerService (даже если приложение не на переднем плане), необходимо объявить GCMReceiver. (Кстати, Google уже предоставляет для него реализацию, поэтому вам нужно только объявить его в манифесте). Более тонкий случай, когда GCMListenerService игнорируется, — это добавление полезной нагрузки notification в сообщение GCM (см. ответ Адама Джонса). - person Peter F; 09.09.2016
comment
Push-уведомление приходит дважды, и gcmlistenerservice не вызывается... и данные уведомления полностью отличаются от предыдущих - person Ramaraju; 06.04.2018