本指南介绍了如何在移动和 Web 客户端应用中设置 Firebase Cloud Messaging,以便您能够可靠地接收消息。
如需接收消息,请使用扩展 FirebaseMessagingService
的服务。您的服务应该重写 onMessageReceived
和 onDeletedMessages
回调方法。
对于大多数消息类型,您都可以使用 onMessageReceived
,但以下情况除外:
当应用在后台时送达的通知消息。在这种情况下,通知将传送至设备的系统任务栏。默认情况下,用户点按通知即可打开应用启动器。
在后台接收的既包含通知又具有数据载荷的消息。在这种情况下,通知将传送至设备的系统任务栏,数据载荷则传送至启动器 Activity 的 intent 的 extras 属性中。
总结:
应用状态 | 通知 | 数据 | 两者 |
---|---|---|---|
前台 | onMessageReceived |
onMessageReceived |
onMessageReceived |
背景 | 系统托盘 | onMessageReceived |
通知:系统任务栏;数据:intent 的 extras 属性中。 |
如需详细了解消息类型,请参阅通知和数据消息。
onMessageReceived
回调会提供超时,但该超时只考虑了您发布通知所需的时间,而没有考虑应用访问网络或执行其他操作所需花费的时间。因此,如果您的应用执行的操作较复杂,则您需要进行额外的配置,来确保应用能够完成任务。
如果您预计应用可能需要接近 10 秒的时间来处理消息,则应安排一个 WorkManager 作业,或遵循下面的 WakeLock 指南执行相应操作。在某些情况下,可用来处理消息的时间窗口可能会短于 10 秒,具体取决于调用 onMessageReceived
之前发生的延迟,包括操作系统延迟以及应用启动时间、主线程被其他操作阻止或先前的 onMessageReceived
调用耗时过长等诸多因素导致的延迟。该时间窗口到期后,您的应用可能会受到进程终止或后台执行限制等行为影响。请注意,网络事务和应用启动的延迟时间可能会较长,因此如有任何异步依赖项(例如网络访问或密集的数据加载要求),请尽量规划较长的时间来运行消息处理任务。
修改应用清单
如需使用 FirebaseMessagingService
,您需要将以下内容添加到您的应用清单:
<service
android:name=".java.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
此外,建议您设置默认值来自定义通知的外观。您可以指定自定义默认图标和颜色,当通知载荷中未设置相应的值时,系统会使用这些默认值。
将以下代码行添加到 application
标记内,以设置自定义默认图标和颜色:
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
对于以下消息,Android 会显示并使用自定义的默认图标:
- 从 Notifications Composer 发送的所有通知消息。
- 未在通知载荷中明确设置图标的通知消息。
如果未设置自定义默认图标,而且通知载荷中也未设置图标,Android 会显示以白色渲染的应用图标。
重写 onMessageReceived
通过重写 FirebaseMessagingService.onMessageReceived
方法,您可以根据收到的 RemoteMessage 对象执行操作并获取消息数据:
Kotlin
override fun onMessageReceived(remoteMessage: RemoteMessage) { // TODO(developer): Handle FCM messages here. // Not getting messages here? See why this may be: https://goo.gl/39bRNJ Log.d(TAG, "From: ${remoteMessage.from}") // Check if message contains a data payload. if (remoteMessage.data.isNotEmpty()) { Log.d(TAG, "Message data payload: ${remoteMessage.data}") // Check if data needs to be processed by long running job if (needsToBeScheduled()) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob() } else { // Handle message within 10 seconds handleNow() } } // Check if message contains a notification payload. remoteMessage.notification?.let { Log.d(TAG, "Message Notification Body: ${it.body}") } // Also if you intend on generating your own notifications as a result of a received FCM // message, here is where that should be initiated. See sendNotification method below. }
Java
@Override public void onMessageReceived(RemoteMessage remoteMessage) { // TODO(developer): Handle FCM messages here. // Not getting messages here? See why this may be: https://goo.gl/39bRNJ Log.d(TAG, "From: " + remoteMessage.getFrom()); // Check if message contains a data payload. if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); if (/* Check if data needs to be processed by long running job */ true) { // For long-running tasks (10 seconds or more) use WorkManager. scheduleJob(); } else { // Handle message within 10 seconds handleNow(); } } // Check if message contains a notification payload. if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); } // Also if you intend on generating your own notifications as a result of a received FCM // message, here is where that should be initiated. See sendNotification method below. }
在处理 FCM 消息期间让设备保持唤醒状态
如果应用需要在处理 FCM 消息期间让设备保持唤醒状态,则需要在此期间持有一个 WakeLock,或是创建一个 WorkManager 作业。对于可能超出 onMessageReceived
默认超时的短时处理活动,WakeLock 非常有效。对于持续较长时间的工作流(例如向服务器发送多个串行 RPC),使用 WorkManager 作业则比使用 WakeLock 更合适。在本部分中,我们将重点介绍如何使用 WakeLock。WakeLock 可防止设备在应用运行时进入休眠状态,不过这可能会导致电池用量增加,因此仅当应用需要在处理消息期间不被暂停的情况下才应使用 WakeLock,比如以下情况:
- 向用户发送时效性通知。
- 与设备外部的事务进行不应中断的交互,例如网络传输或与其他设备(如已配对的手表)通信。
首先,您需要确保应用请求了 WakeLock 权限(FCM SDK 默认包含此权限,因此通常无需添加任何配置)。
<uses-permission android:name="android.permission.WAKE_LOCK" />
然后,应用需要在 FirebaseMessagingService.onMessageReceived()
回调开始时获取 WakeLock,并在回调结束时释放它。
应用的自定义 FirebaseMessagingService
:
@Override
public void onMessageReceived(final RemoteMessage message) {
// If this is a message that is time sensitive or shouldn't be interrupted
WakeLock wakeLock = getSystemService(PowerManager.class).newWakeLock(PARTIAL_WAKE_LOCK, "myApp:messageReceived");
try {
wakeLock.acquire(TIMEOUT_MS);
// handle message
...
finally {
wakeLock.release();
}
}
重写 onDeletedMessages
在某些情况下,FCM 可能不会传递消息。比如说,如果在特定设备连接到 FCM 时,您的应用在该设备上的待处理消息过多(超过 100 条),或者设备超过一个月未连接到 FCM,就会发生这种情况。在这些情况下,您可能会收到对 FirebaseMessagingService.onDeletedMessages()
的回调。当应用实例收到此回调时,应与应用服务器进行一次完全同步操作。如果您在过去 4 周内未向该设备上的应用发送消息,FCM 将不会调用 onDeletedMessages()
。
在后台应用中处理通知消息
当您的应用在后台运行时,Android 会将通知消息传送至系统任务栏。默认情况下,用户点按通知即可打开应用启动器。
此类消息包括既具有通知也具有数据载荷的消息(以及所有从 Notifications 控制台发送的消息)。在这些情况下,通知将传送至设备的系统任务栏,数据载荷则传送至启动器 Activity 的 intent 的 extras 属性中。
如需详细了解发送到您应用的消息,请参阅 FCM 报告信息中心。该信息中心会记录在 Apple 和 Android 设备上发送和打开的消息数量,以及 Android 应用的“展示次数”(用户看到的通知条数)数据。