Kotlin 01.2 版的 Android 進階功能:Android Firebase 雲端通訊

這個程式碼研究室是「Android Kotlin 進階功能」課程的一部分。如果您按部就班完成每一堂程式碼研究室課程,就能充分體驗到本課程的價值,但這不是強制要求。如要查看所有課程程式碼研究室,請前往 Android Kotlin 進階功能程式碼研究室登陸頁面

簡介

先前的程式碼研究室中,您已在應用程式中建立並觸發通知,並將通知新增至蛋定時器。通知的另一個重要用途是遠端傳送推播通知,即使應用程式未執行,使用者也能收到通知。

什麼是推播通知?

推播通知是指伺服器「推送」到行動裝置的通知。無論應用程式是否正在執行,都可以將通知傳送到裝置。

推播通知是通知使用者更新或提醒他們工作或功能的好方法。想像一下,你正在等待某項產品補貨。購物應用程式可透過推播通知告知你最新庫存資訊,不必每天查看庫存狀態。

推播通知採用發布/訂閱模式,讓後端應用程式將相關內容推播給感興趣的用戶端。如果沒有發布/訂閱模式,應用程式使用者就必須定期檢查應用程式更新,這個程序對使用者來說既繁瑣又不穩定。此外,隨著用戶端數量增加,這些定期檢查會對應用程式伺服器和使用者裝置的網路和處理資源造成過大的負擔。

與所有其他類型的通知一樣,請確保推播通知不會打擾使用者。如果通知內容對使用者來說不有趣或不合時宜,他們可以輕鬆關閉應用程式的所有通知。

什麼是 Firebase 雲端通訊?

Firebase 雲端通訊是 Firebase 行動開發平台的一部分,通常您需要從頭開始設定伺服器,以便與行動裝置通訊來觸發通知。有了 Firebase 雲端通訊,您不必設定伺服器,就能將通知傳送給所有已安裝應用程式的使用者,或其中的一部分。例如,你可以傳送提醒給使用者,或提供特別促銷活動,例如贈送免費禮物。你可以從遠端將通知推播至單一或多部裝置。

您也可以使用 Firebase 雲端訊息,將資料從後端應用程式或 Firebase 專案傳送給使用者。

在本程式碼研究室中,您將瞭解如何使用 Firebase 雲端通訊服務,為 Android 應用程式傳送推送通知和資料。

進行本程式碼研究室時,如果你遇到任何問題 (例如程式碼錯誤、文法錯誤或用詞不明確等),請透過程式碼研究室左下角的「回報錯誤」連結回報問題。

必備知識

您必須已經熟悉下列項目:

  • 如何使用 Kotlin 建立 Android 應用程式。特別是 Android SDK。
  • 如何使用架構元件和資料繫結設計應用程式。
  • 對廣播接收器有基本瞭解。
  • 對 AlarmManager 有基本瞭解。
  • 如何使用 NotificationManager 建立及傳送通知。

課程內容

  • 如何透過 Firebase 雲端通訊將訊息推送給使用者。
  • 如何使用資料訊息 (Firebase 雲端通訊的一部分),從後端傳送資料至應用程式。

學習內容

  • 在入門應用程式中新增推播通知。
  • 在應用程式執行期間處理 Firebase 雲端通訊。
  • 透過 Firebase 雲端通訊服務傳輸資料。

在本程式碼研究室中,您將使用先前「在 Android 應用程式中使用通知」程式碼研究室的程式碼。在先前的程式碼研究室中,您建構了雞蛋計時器應用程式,會在烹飪計時器時間到時傳送通知。在本程式碼研究室中,您將新增 Firebase 雲端通訊,向應用程式使用者傳送推送通知,提醒他們吃雞蛋。

如要取得範例應用程式,請執行下列任一動作:

從 GitHub 複製存放區,然後切換至 starter 分支:

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications-fcm


或者,您也可以將存放區下載為 ZIP 檔案、將其解壓縮,並在 Android Studio 中開啟。

下載 ZIP 檔

步驟 1:建立 Firebase 專案

將 Firebase 加入 Android 應用程式前,請先建立要連結至該 Android 應用程式的 Firebase 專案。

  1. 登入 Firebase 控制台
  2. 按一下「新增專案」,然後選取或輸入「專案名稱」。將專案命名為 fcm-codelab
  3. 按一下「繼續」
  4. 如要略過設定 Google Analytics,請關閉「啟用這項專案的 Google Analytics 功能」 按鈕。
  5. 按一下「建立專案」,完成 Firebase 專案設定。

步驟 2:向 Firebase 註冊應用程式

建立 Firebase 專案後,即可加入 Android 應用程式。

  1. Firebase 控制台的專案總覽頁面中間,按一下「Android」Android圖示來啟動設定工作流程。

  1. 在「Android package name」(Android 套件名稱) 欄位中,輸入 com.example.android.eggtimernotifications
  2. 按一下 [Register app] (註冊應用程式)

重要事項: 請務必輸入正確的應用程式 ID,因為向 Firebase 專案註冊應用程式後,就無法新增或修改這個值。

步驟 3:將 Firebase 設定檔新增至專案

將 Firebase Android 設定檔新增至應用程式。

  1. 按一下「Download google-services.json」,取得 Firebase Android 設定檔 (google-services.json)。請確認設定檔未附加其他字元,且名稱完全為 google-services.json
  2. 將設定檔移到應用程式的模組 (應用程式層級) 目錄中。

步驟 4:設定 Android 專案,啟用 Firebase 產品

如要在應用程式中啟用 Firebase 產品,請務必將 google-services 外掛程式新增至 Gradle 檔案。

  1. 在根層級 (專案層級) 的 Gradle 檔案 (build.gradle) 中,確認您有 Google 的 Maven 存放區。
  2. 然後新增規則,加入 Google 服務外掛程式。

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.2'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. 在模組 (應用程式層級) Gradle 檔案 (通常是 app/build.gradle) 中,在檔案底部新增一行,套用外掛程式。

app/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

在這項工作中,您會將 Firebase 雲端通訊 (FCM) 新增至專案,以便使用推播通知。

本程式碼研究室的 FCM Android 服務程式碼位於 MyFirebaseMessagingService.kt。在下列步驟中,您將在 Android 應用程式中新增程式碼。

您將使用通知撰寫工具測試實作項目。通知撰寫工具可協助您透過 Firebase 控制台網站撰寫及傳送訊息。

  1. 開啟 MyFirebaseMessagingService.kt
  2. 檢查檔案,特別是下列函式:
  • onNewToken()—如果服務已在 Android 資訊清單中註冊,系統會自動呼叫。首次執行應用程式時,以及 Firebase 每次為應用程式發出新權杖時,系統都會呼叫這個函式。權杖是 Firebase 後端專案的存取金鑰。這是為特定用戶端裝置產生的,有了這個權杖,Firebase 就能知道後端應將訊息傳送至哪個用戶端。Firebase 也會判斷這個用戶端是否有效,以及是否可存取這個 Firebase 專案。
  • onMessageReceived:應用程式執行時,Firebase 會呼叫這個函式,將訊息傳送至應用程式。這個函式會接收 RemoteMessage 物件,其中可能包含通知或資料訊息酬載。您將在本程式碼研究室稍後的部分,進一步瞭解通知和資料訊息酬載之間的差異。

步驟 1:將 FCM 通知傳送至單一裝置

您可以使用通知控制台測試傳送通知。如要使用控制台傳送訊息給特定裝置,您必須知道該裝置的註冊權杖。

Firebase 後端產生新的或重新整理的權杖時,系統會呼叫 onNewToken() 函式,並將新權杖做為引數傳遞。如要指定單一裝置,或建立要傳送廣播訊息的裝置群組,您必須透過擴充 FirebaseMessagingService 並覆寫 onNewToken(),存取這個權杖。

  1. 開啟 AndroidManifest.xml,取消註解下列程式碼,為 Egg Timer 應用程式啟用 MyFirebaseMessagingService。Android 資訊清單中的服務中繼資料會將 MyFirebaseMessagingService 註冊為服務,並新增意圖篩選器,因此這項服務會接收從 FCM 傳送的訊息。中繼資料的最後一部分會將 breakfast_notification_channel_id 宣告為 Firebase 的 default_notification_channel_id。您將在下一個步驟中使用這個 ID。
<!-- AndroidManifest.xml -->
<!-- TODO: Step 3.0 uncomment to start the service  -->

        <service
                android:name=".MyFirebaseMessagingService"
                android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
        <!-- [START fcm_default_icon] -->
        <!--
 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/common_google_signin_btn_icon_dark"/>
        <!--
 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"/> <!-- [END fcm_default_icon] -->
        <!-- [START fcm_default_channel] -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/breakfast_notification_channel_id" />
        <!-- [END fcm_default_channel] -->

建議您為 FCM 建立新的通知管道,因為使用者可能想分別啟用/停用計時器或 FCM 推送通知。

  1. 開啟 ui/EggTimerFragment.kt。在 onCreateView() 中新增下列管道建立程式碼。
// EggTimerFragment.kt

   // TODO: Step 3.1 create a new channel for FCM
    createChannel(
        getString(R.string.breakfast_notification_channel_id),
        getString(R.string.breakfast_notification_channel_name)
    )
  1. 開啟 MyFirebaseMessagingService.kt 並取消註解 onNewToken() 函式。系統產生新權杖時,就會呼叫這個函式。
// MyFirebaseMessagingService.kt

   // TODO: Step 3.2 log registration token
    // [START on_new_token]
    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the     
     * InstanceID token is initially generated so this is where you would retrieve     
     * the token.
     */
    override fun onNewToken(token: String?) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // Instance ID token to your app server.
        sendRegistrationToServer(token)
    }
    // [END on_new_token]
  1. 執行蛋形計時器應用程式。
  2. 觀察 Logcat (依序點選「View」>「Tool Windows」>「Logcat」)。您應該會看到類似下方的記錄行,顯示您的權杖。這是將訊息傳送至這部裝置時所需的權杖。只有在建立新權杖時,才會呼叫這個函式。
2019-07-23 13:09:15.243 2312-2459/com.example.android.eggtimernotifications D/MyFirebaseMsgService: Refreshed token: f2esflBoQbI:APA91bFMzNNFaIskjr6KIV4zKjnPA4hxekmrtbrtba2aDbh593WQnm11ed54Mv6MZ9Yeerver7pzgwfKx7R9BHFffLBItLEgPvrtF0TtX9ToCrXZ5y7Hd-m

注意: 如果 logcat 訊息中未顯示權杖,表示應用程式可能已收到權杖。在這種情況下,解除安裝應用程式有助於取得新權杖。

現在你可以傳送通知進行測試。如要傳送通知,請使用通知撰寫器

  1. 開啟 Firebase 控制台並選取專案。
  2. 接著,選取左側導覽中的「Cloud Messaging」
  3. 按一下「傳送第一則訊息」

  1. 輸入 Time for Breakfast! 做為通知標題,並輸入 Don't forget to eat eggs! 做為通知文字,然後選取「傳送測試訊息」。畫面上會顯示「在裝置上測試」彈出式對話方塊,要求您提供 FCM 註冊權杖。

  1. 從 logcat 複製應用程式符記。

  1. 將這個權杖貼到彈出式視窗中的「新增 FCM 註冊權杖」欄位,然後按一下權杖旁邊的「新增」按鈕。
  2. 在隨即顯示的核取方塊清單中,選取權杖。「測試」按鈕應會啟用。

  1. 在裝置上將「計時器」應用程式移到背景
  2. 在彈出式視窗中,按一下「測試」
  1. 按一下「測試」後,目標用戶端裝置的系統通知匣應會收到通知,前提是該裝置在背景執行您的應用程式。(稍後會進一步說明如何在應用程式於前景運作時處理 FCM 訊息)。

工作:將 FCM 通知傳送至主題

FCM 主題訊息功能採用發布/訂閱模型。

訊息應用程式是 發布/訂閱模型的絕佳範例。假設應用程式每 10 秒檢查一次新訊息,這不僅會耗盡手機電量,還會使用不必要的網路資源,並對應用程式伺服器造成不必要的負擔。用戶端裝置可以訂閱,並在透過應用程式傳送新訊息時收到通知。

主題可讓您將訊息傳送給已選擇接收特定主題訊息的多部裝置。對客戶而言,主題是客戶感興趣的特定資料來源。對伺服器而言,主題是選擇接收特定資料來源更新的裝置群組。主題可用於呈現通知類別,例如新聞、天氣預報和體育賽事結果。在本程式碼研究室的這部分,您將建立「早餐」主題,提醒感興趣的應用程式使用者在早餐時吃雞蛋。

如要訂閱主題,用戶端應用程式會使用主題名稱 breakfast 呼叫 Firebase 雲端通訊 subscribeToTopic() 函式。這項呼叫可能有兩種結果。如果呼叫端成功,系統會使用訂閱的訊息呼叫 OnCompleteListener 回呼。如果用戶端無法訂閱,回呼會收到錯誤訊息。

在應用程式中,系統會自動為使用者訂閱早餐主題。不過,在大多數正式版應用程式中,最好讓使用者控管要訂閱的主題。

  1. 開啟 EggTimerFragment.kt 並找出空白的 subscribeTopic() 函式。
  2. 取得 FirebaseMessaging 的例項,並使用主題名稱呼叫 subscibeToTopic() 函式。
  3. 新增 addOnCompleteListener,接收 FCM 傳回的通知,瞭解訂閱作業是否成功。
// EggTimerFragment.kt

   // TODO: Step 3.3 subscribe to breakfast topic
    private fun subscribeTopic() {
        // [START subscribe_topics]
        FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
            .addOnCompleteListener { task ->
                var msg = getString(R.string.message_subscribed)
                if (!task.isSuccessful) {
                    msg = getString(R.string.message_subscribe_failed)
                }
                Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
            }
        // [END subscribe_topics]
    }
  1. 應用程式啟動時,請呼叫 subscribeTopic() 函式來訂閱主題。向上捲動至 onCreateView(),然後呼叫 subscribeTopic()
// EggTimerFragment.kt

   // TODO: Step 3.4 call subscribe topics on start
    subscribeTopic()

    return binding.root
  1. 如要訂閱早餐主題,請再次執行應用程式。畫面上應會顯示「已訂閱主題」的訊息。

現在您可以測試將訊息傳送至主題:

  1. 開啟通知撰寫工具,然後選取「撰寫通知」
  2. 如先前所述,設定通知的通知標題通知文字
  3. 這次請點按「目標」下方的「主題」,然後輸入 breakfast 做為訊息主題,而不是將訊息傳送至單一裝置。

  1. 選取「立即」安排時間。

  1. 確認應用程式在測試裝置上於背景執行。
  1. 按一下「檢查」,然後按一下「發布」。如果可以在多部裝置上執行應用程式,您可以測試並觀察訂閱這個主題的所有裝置是否都收到通知。

應用程式現在有「Egg」和「Breakfast」這兩個通知管道。在用戶端裝置上長按應用程式圖示,選取「資訊」,然後點選「通知」。您應該會看到「Egg」和「Breakfast」通知管道,如下方螢幕截圖所示。如果取消選取「早餐」頻道,應用程式就不會收到透過這個頻道傳送的任何通知。

使用通知時,請務必注意使用者隨時可以關閉任何通知管道。

步驟 1:資料訊息

FCM 訊息也可以包含資料酬載,用於處理用戶端應用程式中的訊息,請改用資料訊息,而非通知訊息。

如要處理資料訊息,您需要在 MyFirebaseMessagingServiceonMessageReceived() 函式中處理資料酬載。酬載會儲存在 remoteMessage 物件的 data 屬性中。remoteMessage 物件和 data 屬性都可以是 null

  1. 開啟「MyFirebaseMessagingService.
  2. 檢查 remoteMessage 物件的 data 屬性是否具有某些值,並將資料列印至記錄檔。
// MyFirebaseMessagingService.kt

    // [START receive_message]
    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
        Log.d(TAG, "From: ${remoteMessage?.from}")
        
       // TODO: Step 3.5 check messages for data
        // Check if the message contains a data payload.
        remoteMessage?.data?.let {
            Log.d(TAG, "Message data payload: " + remoteMessage.data)
        }

    }
    // [END receive_message]

如要測試程式碼,可以再次使用通知撰寫工具

  1. 開啟通知撰寫工具,建立新訊息,並將「目標」 設為「breakfast」主題。
  2. 這次在步驟 4「其他選項」中,請將「自訂資料」鍵和值屬性設為:
  1. 鍵: eggs
  2. 值: 3

  1. 確認應用程式在前景執行。如果應用程式在背景執行,FCM 訊息會觸發自動通知,且使用者點選通知時,onMessageReceived() 函式只會收到 remoteMessage 物件。
  2. 從「通知」撰寫工具傳送訊息,並觀察 logcat 中顯示的資料訊息記錄。

步驟 2:處理前景和背景中的訊息

如果執行您應用程式的用戶端裝置收到同時包含通知和資料酬載的訊息,應用程式的行為會視該裝置上應用程式是在背景或前景執行而定:

  • 如果應用程式在背景執行,且訊息含有通知酬載,通知就會自動顯示在通知匣中。如果訊息也包含資料酬載,使用者輕觸通知時,應用程式會處理資料酬載。
  • 如果應用程式在前台執行,且訊息通知含有通知酬載,系統就不會自動顯示通知。應用程式需要在 onMessageReceived() 函式中決定如何處理通知。如果訊息也含有資料酬載,應用程式會處理這兩種酬載。

在本程式碼研究室中,您要提醒應用程式使用者吃雞蛋當早餐。您不打算傳送任何資料,但希望無論應用程式是在前景或背景執行,系統一律會顯示提醒通知。

當您將 FCM 訊息傳送至已安裝計時器應用程式的裝置時,如果應用程式未執行或在背景執行,系統就會自動顯示通知訊息。不過,如果應用程式在前台執行,系統就不會自動顯示通知,而是由應用程式的程式碼決定如何處理訊息。如果應用程式在收到 FCM 訊息時處於前景,系統會自動觸發 onMessageReceived() 函式並傳遞 FCM 訊息。應用程式可以在這裡以無聲方式處理通知和資料酬載,或觸發通知。

您希望使用者在應用程式處於前景時收到提醒,因此請實作一些程式碼來觸發通知:

  1. 再次開啟 MyFirebaseMessagingService 中的 onMessageReceived() 函式。
  2. 在您最近新增的程式碼後方,立即加入下列程式碼,使用通知架構傳送通知。
// MyFirebaseMessagingService.kt

    // TODO: Step 3.6 check messages for notification and call sendNotification
    // Check if the message contains a notification payload.
    remoteMessage.notification?.let {
        Log.d(TAG, "Message Notification Body: ${it.body}")
        sendNotification(it.body as String)
    }
  1. 再次執行應用程式並使用 Notifications Composer 傳送通知時,無論應用程式是在前景或背景執行,您都應該會看到通知,就像在程式碼研究室的第一部分一樣。

解決方案程式碼位於下載程式碼的主要分支

Udacity 課程:

Firebase 說明文件:

如要查看本課程其他程式碼研究室的連結,請參閱 Android Kotlin 進階功能程式碼研究室登陸頁面