使用 Firebase 雲端通訊接收訊息

本指南說明如何在行動和網頁用戶端應用程式中設定 Firebase Cloud Messaging,以便穩定接收訊息。

如要接收訊息,請使用擴充 FirebaseMessagingService 的服務。您的服務應覆寫 onMessageReceivedonDeletedMessages 回呼。

onMessageReceived 適用於大多數郵件類型,但下列情況除外:

  • 應用程式在背景運作時傳送的通知訊息。在這種情況下,通知會傳送到裝置的系統匣。使用者輕觸通知後,系統預設會開啟應用程式啟動器。

  • 同時包含通知和資料酬載的訊息,在背景收到時。在這種情況下,通知會傳送到裝置的系統匣,資料酬載則會傳送到啟動器活動意圖的額外內容。

簡單來說:

應用程式狀態 通知 資料 兩者並用
前景 onMessageReceived onMessageReceived onMessageReceived
背景 系統匣 onMessageReceived 通知:系統匣 資料:意圖的額外內容。

如要進一步瞭解訊息類型,請參閱「通知和資料訊息」。

系統會為 onMessageReceived 回呼提供逾時時間,讓您發布通知,但計時器並非用於允許應用程式存取網路或執行額外工作。因此,如果應用程式執行更複雜的作業,您需要額外進行一些工作,確保應用程式能完成作業。

如果應用程式處理訊息的時間可能接近 10 秒,請安排 WorkManager 工作或按照下方的 WakeLock 指南操作。在某些情況下,處理訊息的時間視窗可能短於 10 秒,具體取決於呼叫 onMessageReceived 前發生的延遲,包括 OS 延遲、應用程式啟動時間、主執行緒遭其他作業阻斷,或先前的 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 會顯示並使用自訂預設圖示,

  • 從「通知撰寫工具」傳送的所有通知訊息。
  • 未在通知酬載中明確設定圖示的任何通知訊息。

如果未設定自訂預設圖示,且通知酬載中未設定圖示,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 預設逾時時間,建議使用 WakeLocks 處理短時間的活動。如果是擴充工作流程 (例如將多個序列 RPC 傳送至伺服器),使用 WorkManager 工作會比 WakeLock 更合適。本節將著重說明如何使用 WakeLock。應用程式執行時,WakeLock 會防止裝置進入休眠狀態,這可能會導致電池用量增加,因此 WakeLock 應保留給應用程式在處理訊息時不應暫停的情況,例如:

  • 具時效性的使用者通知。
  • 與裝置外部的項目互動,不應中斷 (例如網路傳輸或與其他裝置通訊,如已配對的手錶)。

首先,請確認應用程式要求 WakeLock 權限 (FCM SDK 預設會包含這項權限,因此通常不需要新增任何內容)。

<uses-permission android:name="android.permission.WAKE_LOCK" />

然後,應用程式必須在 FirebaseMessagingService.onMessageReceived() 回呼開始時取得 WakeLock,並在回呼結束時釋放 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 可能無法傳送訊息。如果裝置連線時,應用程式在特定裝置上待處理的訊息過多 (超過 100 則),或是裝置超過一個月未連線至 FCM,就會發生這種情況。在這些情況下,您可能會收到 FirebaseMessagingService.onDeletedMessages() 的回呼。應用程式執行個體收到這項回呼時,應與應用程式伺服器執行完整同步。如果過去 4 週內未透過該裝置傳送訊息,FCM就不會撥打電話onDeletedMessages()

處理背景應用程式中的通知訊息

當應用程式在背景執行時,Android 會將通知訊息導向系統匣。根據預設,使用者輕觸通知後會開啟應用程式啟動器。

包括同時含有通知和資料酬載的訊息 (以及從「通知」控制台傳送的所有訊息)。在這些情況下,通知會傳送至裝置的系統匣,資料酬載則會傳送至啟動器 Activity 的 Intent Extras。

如要深入瞭解訊息傳送至應用程式的情況,請參閱 FCM 報表資訊主頁,其中會記錄在 Apple 和 Android 裝置上傳送及開啟的訊息數量,以及 Android 應用程式的「曝光次數」(使用者看到的通知) 資料。