GcmListenerService.onMessageReceived()未被调用

14

我目前正在实现将GCM通知集成到我的应用程序中。

我遇到的问题是,我的GcmListenerService实现中的onMessageReceived()方法没有被调用。我可以正常地从GCM服务器接收数据,因为它会自动生成一个通知(我希望使用onMessageReceived()方法替换它并自己创建通知),但在此之后,我的日志调用都不会打印在日志中。

从服务器发送到GCM服务器的JSON

{
    "notification" : {
        "title" : "Title",
        "text" : "Message",
        "icon" : "@drawable\/ic_notification",
        "click_action" : "OPEN_MAIN_ACTIVITY"
    },
    "registration_ids":[
        "xxxx", "xxxx", "xxxx", "etc"
    ]
}

AndroidManifest.xml(仅GCM部分)

<!-- GCM START -->
    <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.my.package" />
        </intent-filter>
    </receiver>

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

    <service
        android:name=".Services.IDListenerService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.android.gms.iid.InstanceID"/>
        </intent-filter>
    </service>
    <!-- GCM END -->

GcmListenerService(只是一个快速打印以查看是否被调用)

public class ListenerService extends GcmListenerService {

    private static final String TAG = "MyGcmListenerService";

    @Override
    public void onMessageReceived(String from, Bundle data) {
        String message = data.getString("title");
        Log.d(TAG, "From: " + from);
        Log.d(TAG, "Message: " + message);
    }
}

如果需要,我可以发布请求令牌的方法,但不确定是否相关。

如果问题的任何部分不清楚,请告诉我,我并不擅长解释。


你注册了你的客户端吗? - Gordak
我正在接收一个令牌和通知(我认为GCM自动生成它们或类似的东西),如果这是你的意思? - Dan
如果您按照https://developers.google.com/cloud-messaging/android/client设置了客户端,那么应该没问题。不要忘记将您的GCM令牌发送到您的服务器应用程序(我在这里没有看到注册代码)。 - Gordak
我按照那个设置进行了操作,我将令牌发送到服务器并使用服务器发送通知,唯一的问题是当接收到通知时onMessageReceived()方法没有被调用。即使我删除整个Listener类,它也会自己显示通知。 - Dan
我看不到任何错误。我猜你用你真实的包名替换了“com.my.package”。我只会将包名“Services”重命名为“services”。 - Gordak
1
@Dan 当你从服务器的负载中发送“notification”键时,onMessageReceived不会被调用,你必须从Intent中获取数据。只有当你使用“data”键时,onMessageReceived才会被调用。你可以同时使用两者,但是你需要从不同的位置获取每个方法中的数据。 - Alex Bitek
4个回答

12
根据此Github问题的解释,这正是您所遇到的问题:
根据https://developers.google.com/cloud-messaging/server#notifications_and_data_messages: "GCM将代表客户端应用程序显示通知部分。提供可选数据时,用户单击通知并打开客户端应用程序后,它将发送到客户端应用程序。 [...] 在Android上,可以在用于启动活动的Intent中检索数据有效负载。"

因此,在用户点击通知后,数据将通过用于启动活动的Intent传递。 这意味着您需要执行以下操作:
  • 向从服务器发送的通知键添加click_action:

    send_queue.append({'to': REGISTRATION_ID,
                   'message_id': random_id(),
                   "notification" : {
                      "body" : "Hello from Server! What is going on? Seems to work!!!",
                      "title" : "Hello from Server!",
                      "icon" : "@drawable/ic_school_white_48dp",
                      "sound": "default",
                      "color": "#03A9F4",
                      "click_action": "OPEN_MAIN_ACTIVITY"
                    },
                   'data': { 'message': "Hello" }})
    

请参阅有关通知有效负载的参考文档:https://developers.google.com/cloud-messaging/server-ref#notification-payload-support

  • AndroidManifest.xml中,在您希望用户单击通知后打开的活动上添加一个意图过滤器,该过滤器与您在服务器端使用的“click_action”键相同,例如:

<activity
    android:name=".ui.MainActivity"
    android:label="@string/title_activity_main" >
    <intent-filter>
        <action android:name="OPEN_MAIN_ACTIVITY" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
  • 如果您将要启动的活动的launchMode设置为singleTop,并且希望单击通知时启动它,请在您的onCreate()方法onNewIntent()中获取来自意图的数据,例如:

  • @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        Intent intent = getIntent();
    
        if (intent.hasExtra(Constants.KEY_MESSAGE_TXT)) {
            String message = intent.getStringExtra(Constants.KEY_MESSAGE_TXT);
            Log.d(TAG, message);
        } 
    }
    
    我已经测试过并确认它可以工作。(使用XMPP连接)

    解决了我的问题,我之前遇到过那个 Github 问题,但显然没有仔细阅读,感谢您的澄清。有没有办法在客户端更新通知?就是不要收到4个相同的通知,而只收到一个?类似于这样:http://imgur.com/a/DlJV0 - Dan
    2
    @Dan 如果您使用通知键,可以为每个通知设置“标记”键,该键与先前的通知相同,并且具有相同标记的新通知将替换通知中心中的先前通知。请参见此处的“标记”键说明(https://developers.google.com/cloud-messaging/server-ref#notification-payload-support)。还要查看“collapse_key”和“delay_while_idle”,这些对于告诉GCM在无法立即传递通知并重试时发生的情况非常有用。 - Alex Bitek
    2
    如果您不使用通知键,而是使用“数据”键,则需要通过跟踪先前显示的通知来自行进行排除。 - Alex Bitek
    1
    你太棒了,如果可以的话,我会给你100票。谢谢你的帮助。 - Dan

    4

    如果下行消息(json)包含通知,则GcmListenerService.onMessageReceived()不会被调用。


    2
    为了阐述@Zephyr的观点,GCM消息中有两种负载类型:通知消息和数据消息。如果应用程序处于非活动状态,通知消息不会调用onMessageReceived方法,它们只会在通知栏中显示通知。而数据消息将始终调用onMessageReceived方法,无论应用程序的状态如何。详细信息请参见此处 - Shiprack

    3
    要在onMessageReceived中接收消息,您需要在消息对象中定义顶级“data”字段。通知字段会自动处理并生成通知,onMessageReceived不会传递任何通知字段中的数据。
    更新您的消息对象以包括一个数据字段,这样就会调用onMessageReceived:
    {
        "notification" : {
            "title" : "标题",
            "text" : "消息",
            "icon" : "@drawable\/ic_notification",
            "click_action" : "OPEN_MAIN_ACTIVITY"
        },
        "data": {
            "some_key": "some_value"
        },
        "registration_ids":[
            "xxxx", "xxxx", "xxxx", "等等"
        ]
    }
    

    2
    今晚回家后我会尝试一下。有没有办法在Android上使通知栏不自动生成通知,以便我可以使用自己的实现?最好不要删除整个通知栏,因为我在iOS中使用它。 - Dan
    我想不出一个好的方法来做这件事。你可以尝试省略图标字段,因为它在Android上是必需的,但在iOS上不是。所以我认为它会在Android上被忽略,但你仍然会在onMessageReceived中得到数据部分。 - Arthur Thompson
    1
    完全删除“通知”块就解决了问题,并使电话调用onMessageReceived。虽然不是我想要的方式,但我会找到一些方法来处理它。您知道是否可以将多个通知折叠成一个,以便用户不会收到垃圾邮件式的通知吗?请参见http://imgur.com/a/DlJV0以获取图像示例(类似)。 - Dan
    1
    如果你所说的“被垃圾邮件”,是指通知区域中有多个通知,那么如果这些通知具有相同的通知ID,则最后一个将替换前面的通知。要停止后续通知,您需要检查某个变量是否已经设置了通知。 - Arthur Thompson
    1
    @Dan 你不必删除通知键来获取数据。如果你使用通知键,你只需在不同的位置获取其中的数据,详见下面我的回答。 - Alex Bitek

    0

    onMessageReceived 仅针对“仅数据”推送消息调用。


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