当应用程序在后台时,GcmListenerService未被调用

13

当应用程序处于后台、手机被锁定或休眠模式时,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>

我在这里缺少什么。


如果您看到通知,那么这意味着 Gcm 监听器服务的 onMessageReceived 被调用了?在这里不被调用是什么意思? - Much Overflow
1
当应用程序在前台时,它会落在onMessageReceived中,但是当应用程序在后台时,它不会落在onMessageReceived中。 - Usman Kurd
你正在从 onMessageReceived 方法中调用 sendNotification 方法。你说当应用程序在后台时仍然看到通知,这意味着 onMessageReceived 被执行以显示通知? - Much Overflow
@MuchOverflow 并不一定会在你看到通知时调用onMessageReceived。这取决于应用服务器发送给GCM的有效载荷。请查看我的答案以获取详细信息。 - Adam Johns
2个回答

34
这个问题的核心似乎是一个服务器端问题。如果服务器发送的是“通知”消息,当应用程序处于后台时,“onMessageReceived”不会被调用。实际上,服务器应该发送“数据”消息。请参考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文档指出,发送包含数据通知负载的消息将根据应用程序是否在前台或后台而被不同地处理:
当应用程序处于后台时,它们将在通知托盘中接收到通知负载,并且仅在用户点击通知时处理数据负载。
当应用程序处于前台时,您的应用程序将接收到一个包含两个有效负载的捆绑包。
这个GitHub线程也有一个很好的解释:
因此,有两种类型的GCM消息:
通知消息 - 这些消息旨在生成一个通知,应用程序不需要进行任何中间处理。只有当应用程序正在运行时,它们才会触发onMessageReceived方法。
数据消息 - 这些消息旨在将数据静默传递给应用程序的消息服务。即使应用程序在后台运行,它们也会触发onMessageReceived方法。然后,该服务可以选择使用常规系统通知API生成通知,或者它可以选择静默处理消息。

1
我以前不知道通知的有效载荷。我一直使用数据来发送通知给我的应用程序。谢谢你指出来 :) - Much Overflow
这样的评论肯定值得超过1个赞。 - Syed Muhammad Oan
只是提一下,如果你把它们都添加进去,Android 将被视为通知(示例)https://gist.github.com/eonoe/8a611cac77fdea68c6a4b0d177177225 - eOnOe
哦我的天啊!您救了我的一天,先生。非常感谢您! - flagg327

12

我遇到的问题是当应用程序在后台运行或被强制关闭时,通知不通过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>

这不是正确的答案。Adam Johns讲解了如何在后台模式(或应用程序被杀死时)处理通知。这只是一个JSON问题。 - Francesco
总结一下:为了始终在GCMListenerService中处理GCM消息(即使应用程序不在前台),必须声明一个GCMReceiver。(顺便说一句,Google现在提供了它的实现,所以你只需要在清单中声明它)。更微妙的情况是,如果将“通知”负载添加到GCM消息中,则会绕过GCMListenerService(请参见Adam Johns的答案)。 - Peter F
推送通知出现了两次,gcmlistenerservice没有调用...而且通知数据与以前完全不同。 - Ramaraju

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接