Di chuyển từ FCM HTTP v1 API sang AndroidConfigV2

Khi sử dụng FCM AndroidConfigV2, bạn có thể gửi thông báo dựa trên ý định đến các thiết bị Android bằng cách gửi thông báo kịp thời và API đơn giản hoá.

Hướng dẫn này trình bày cách di chuyển các yêu cầu gửi của phần phụ trợ từ AndroidConfig sang AndroidConfigV2 và cách cập nhật ứng dụng Android từ cách xử lý onMessageReceived cũ sang lệnh gọi lại ứng dụng v2 mới onNotificationReceived và onBackgroundSyncReceived.

Tính năng mới trong AndroidConfigV2

API FCM AndroidConfigV2 đơn giản hoá trải nghiệm của nhà phát triển bằng cách giới thiệu hai loại thông báo:

  • RemoteNotification: Bạn nên dùng cho tất cả thông báo FCM hiển thị thông báo khẩn cấp, có giới hạn thời gian mà người dùng có thể nhìn thấy. Những thông báo này bỏ qua mọi biện pháp tiết kiệm pin và được gửi ngay lập tức. Trên thiết bị, SDK FCM và Dịch vụ Google Play sẽ chịu trách nhiệm đăng thông báo lên NotificationManager của Android.
  • BackgroundSync: Thông báo dữ liệu không khẩn cấp dùng để đồng bộ hoá trong nền và cập nhật trạng thái ứng dụng. Quá trình phân phối có thể được thực hiện theo lô hoặc bị trì hoãn để tối ưu hoá thời lượng pin.
Tính năng Cũ (AndroidConfig phiên bản 1) Mới (AndroidConfigV2)
Trường cấu hình message.android message.android_v2
Định nghĩa về mức độ ưu tiên priority: "high" hoặc priority: "normal" tường minh Được ngụ ý theo loại hợp nhất (remote_notification cho cảnh báo khẩn cấp so với background_sync cho đồng bộ hoá không khẩn cấp)
Đăng thông báo Ứng dụng gọi NotificationManager.notify() theo cách thủ công SDK FCM và GMS Core đăng trực tiếp hoặc sử dụng NotificationResponse
Lệnh gọi lại của ứng dụng • onMessageReceived(RemoteMessage)
• onDeletedMessages()
• onNotificationReceived(RemoteMessage)
• onBackgroundSyncReceived(RemoteMessage)
• onDeletedMessages()
Khả năng thu gọn Thay đổi; cần có chế độ cài đặt rõ ràng Cả hai loại thông báo đều có thể thu gọn hoặc không thu gọn (không thu gọn theo mặc định)

Uỷ quyền cho AndroidConfigV2 gửi yêu cầu

Bạn có thể tiếp tục sử dụng mã truy cập OAuth 2.0 bắt nguồn từ tài khoản dịch vụ Firebase của mình bằng cách sử dụng Thông tin xác thực mặc định của ứng dụng Google hoặc SDK dành cho quản trị viên, đồng thời gửi yêu cầu POST đến điểm cuối HTTP phiên bản 1 messages:send.

Nếu bạn không sử dụng tài khoản dịch vụ Firebase, hãy nhớ chỉ định tài khoản chuyên dụng của bạn cho vai trò Quản trị viên API Giải pháp gửi thông báo qua đám mây của Firebase (roles/firebase.messagingAdmin) và yêu cầu phạm vi OAuth 2.0 https://www.googleapis.com/auth/firebase.messaging khi tạo mã truy cập:

POST https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send HTTP/1.1
Authorization: Bearer YOUR_OAUTH_ACCESS_TOKEN
Content-Type: application/json

Yêu cầu gửi thông tin cập nhật đến máy chủ (REST)

Để gửi yêu cầu AndroidConfigV2 bằng API REST, hãy thay thế đối tượng android trong nội dung yêu cầu bằng android_v2. Trong android_v2, hãy chỉ định các tham số phổ biến (chẳng hạn như ttl, collapse_key, data và restricted_package_name) rồi cung cấp một trong các trường hợp nhất sau: remote_notification hoặc background_sync.

Gửi thông báo RemoteNotification

Để gửi thông báo khẩn cấp dành cho người dùng, hãy thêm đối tượng remote_notification và tải trọng notification được đề xuất. Theo mặc định, mutable_content được đặt thành false. GMS Core ngay lập tức kết xuất và đăng thông báo lên khay hệ thống mà không cần thực thi mã ứng dụng nền.

Mẫu JSON hoàn chỉnh của yêu cầu HTTP (Thông báo được hiển thị tự động)

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
  "message": {
    "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification": {
      "title": "FCM Message",
      "body": "This is an FCM notification message!"
    },
    "android_v2": {
      "ttl": "3600s",
      "remote_notification": {
        "mutable_content": false,
        "notification": {
          "title": "Order update",
          "body": "Your order has been shipped!",
          "icon": "ic_notification_custom",
          "sound": "default",
          "tag": "alert_tag_1",
          "id": 100
        }
      }
    }
  }
}

Gửi RemoteNotification có nội dung có thể thay đổi

Nếu ứng dụng của bạn cần sửa đổi thông báo trên thiết bị trước khi thông báo đó xuất hiện, hãy đặt mutable_content: true bên trong remote_notification. Thao tác này hướng dẫn GMS Core liên kết với ứng dụng khách của bạn và gọi onNotificationReceived(). Điều này rất hữu ích khi bạn muốn giải mã tải trọng được mã hoá hai đầu, đính kèm hình đại diện của người dùng hoặc bản địa hoá chuỗi.

Hãy nhớ thêm đối tượng notification vào bên trong remote_notification ngay cả khi mutable_content là true. GMS Core sử dụng đối tượng notification này làm thông báo dự phòng nếu lệnh gọi lại của ứng dụng khách hết thời gian chờ hoặc vượt quá giới hạn chặn.

Nếu bạn đang di chuyển từ thông báo dữ liệu phiên bản 1 cũ và muốn FCM phân phối tải trọng đến các bản cài đặt ứng dụng khách phiên bản 1 cũ hơn dưới dạng thông báo dữ liệu thuần tuý, hãy đặt use_as_v1_data_message: true bên trong remote_notification.

Mẫu JSON hoàn chỉnh của yêu cầu HTTP

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
  "message": {
    "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data": {
      "encrypted_content": "U2FsdGVkX19vN2m+",
      "sender_id": "user_98124"
    },
    "android_v2": {
      "ttl": "3600s",
      "remote_notification": {
        "mutable_content": true,
        "use_as_v1_data_message": true,
        "notification": {
          "title": "New message",
          "body": "You have a new message",
          "tag": "chat_alert",
          "id": 1001
        }
      }
    }
  }
}

Gửi thông báo BackgroundSync

Để gửi thông báo không khẩn cấp cho việc đồng bộ hoá dữ liệu trong nền, cập nhật trạng thái im lặng hoặc cập nhật huy hiệu hay tiện ích của ứng dụng, hãy sử dụng đối tượng background_sync. Đừng sử dụng các thông báo này để đăng thông báo mà người dùng có thể nhìn thấy, vì các thông báo này có thể được gộp theo lô hoặc bị trì hoãn để tối ưu hoá thời lượng pin của thiết bị.

Mẫu JSON hoàn chỉnh của yêu cầu HTTP

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
  "message": {
    "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data": {
      "score": "FETCH_UPDATES",
      "sync_action": "FETCH_UPDATES"
    },
    "android_v2": {
      "ttl": "86400s",
      "collapse_key": "app_state_sync",
      "background_sync": {}
    }
  }
}

Cập nhật lệnh gọi lại ứng dụng Android

Để nhận thông báo AndroidConfigV2 trên Android, hãy nâng cấp ứng dụng của bạn để sử dụng SDK firebase-messaging mới nhất. Cập nhật dịch vụ mở rộng FirebaseMessagingService bằng cách triển khai onNotificationReceived() và onBackgroundSyncReceived().

Cài đặt SDK EAP

Trong Chương trình tiếp cận sớm (EAP), SDK firebase-messaging phiên bản 2 được phân phối dưới dạng một bản lưu trữ kho lưu trữ Maven cục bộ thay vì thông qua kho lưu trữ Maven công khai của Google.

  1. Thêm thư mục m2repository chứa firebase-messaging:26.1.0-eap01 vào thư mục gốc của dự án Android (<project-root>/m2repository) bằng một trong các lựa chọn sau:

    1. Cách A: Tải bản lưu trữ tạo sẵn xuống: Tải FirebaseMessaging_26.1.0-eap01.zip xuống rồi giải nén thư mục m2repository vào thư mục gốc của dự án Android (<project-root>/m2repository).
    2. Lựa chọn B: Tạo từ GitHub bằng Gradle: Sao chép nhánh feature/fcm-android-config-v2-eap của firebase/firebase-android-sdk, tạo kho lưu trữ Maven cục bộ và sao chép thư mục build/m2repository đã tạo vào thư mục gốc của dự án Android:

      git clone --branch feature/fcm-android-config-v2-eap --single-branch \
        https://github.com/firebase/firebase-android-sdk.git
      cd firebase-android-sdk
      ./gradlew -PprojectsToPublish="firebase-messaging" firebasePublish
      cp -R build/m2repository /path/to/your/android-project/m2repository
      
  2. Trong tệp settings.gradle.kts (hoặc settings.gradle) cấp dự án, hãy thêm thư mục m2repository cục bộ vào đầu khối repositories, phía trên google():

    Kotlin

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            maven { url = uri("${rootDir}/m2repository") }
            google()
            mavenCentral()
        }
    }
    

    Groovy

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            maven { url "${rootDir}/m2repository" }
            google()
            mavenCentral()
        }
    }
    
  3. Trong tệp app/build.gradle.kts (hoặc app/build.gradle) ở cấp mô-đun, hãy cập nhật phần phụ thuộc firebase-messaging thành 26.1.0-eap01:

    Kotlin

    dependencies {
        implementation("com.google.firebase:firebase-messaging:26.1.0-eap01")
    }
    

    Groovy

    dependencies {
        implementation 'com.google.firebase:firebase-messaging:26.1.0-eap01'
    }
    
  4. Đồng bộ hoá dự án với các tệp Gradle.

Xử lý RemoteNotifications bằng onNotificationReceived

Với AndroidConfigV2, thông báo RemoteNotification sẽ hiển thị trừ phi mutable_content được đặt thành true hoặc bị tắt tiếng rõ ràng trong onNotificationReceived().

Khi RemoteNotification đến cùng với mutable_content: true, GMS Core sẽ liên kết với ứng dụng của bạn và gọi onNotificationReceived(). Phương thức của bạn phải trả về một NotificationResponse trong vòng 20 giây.

Trả về thông báo đã sửa đổi

Đối tượng Notification mà bạn truyền đến NotificationResponse.create sẽ thay thế các thuộc tính tải trọng dự phòng remote_notification.notification của máy chủ được xác định trong yêu cầu gửi. Để giữ các giá trị từ yêu cầu gửi, hãy đọc các giá trị đó từ message.notification (message.getNotification() trong Java) trong khi áp dụng các nội dung sửa đổi trên thiết bị:

Kotlin

import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.NotificationResponse
import com.google.firebase.messaging.RemoteMessage

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onNotificationReceived(message: RemoteMessage): NotificationResponse {
        val serverNotif = message.notification
        val channelId = serverNotif?.channelId ?: "default_channel"
        // Keep the server-defined title unchanged, or fall back to default
        val title = serverNotif?.title ?: "New message"
        // Modify the notification body on-device (for example, decrypting data)
        val decryptedBody = decryptPayload(message.data["encrypted_content"])

        val builder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.ic_stat_notification)
            .setContentTitle(title)
            .setContentText(decryptedBody)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)

        // Return the response for the open-source FCM SDK to post to the Android platform
        return NotificationResponse.create("optional_tag", 100, builder.build())
    }
}

Java

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.google.firebase.messaging.NotificationResponse;
import androidx.core.app.NotificationCompat;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public NotificationResponse onNotificationReceived(RemoteMessage message) {
        RemoteMessage.Notification serverNotif = message.getNotification();
        String channelId = (serverNotif != null && serverNotif.getChannelId() != null)
                ? serverNotif.getChannelId()
                : "default_channel";
        // Keep the server-defined title unchanged, or fall back to default
        String title = (serverNotif != null && serverNotif.getTitle() != null)
                ? serverNotif.getTitle()
                : "New message";
        // Modify the notification body on-device (for example, decrypting data)
        String decryptedBody = decryptPayload(message.getData().get("encrypted_content"));

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
                .setSmallIcon(R.drawable.ic_stat_notification)
                .setContentTitle(title)
                .setContentText(decryptedBody)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setAutoCancel(true);

        // Return the response for the open-source FCM SDK to post to the Android platform
        return NotificationResponse.create("optional_tag", 100, builder.build());
    }
}

Ẩn thông báo

Nếu người dùng hiện đang mở màn hình hoặc cuộc trò chuyện có liên quan, bạn có thể chặn thông báo bằng cách trả về NotificationResponse.suppress():

Kotlin

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner

override fun onNotificationReceived(message: RemoteMessage): NotificationResponse {
    val chatId = message.data["chat_id"]
    val isAppForeground = ProcessLifecycleOwner.get()
        .lifecycle
        .currentState
        .isAtLeast(Lifecycle.State.STARTED)

    if (isAppForeground && chatRepository.isConversationActive(chatId)) {
        // Suppress notification display because the user is actively viewing this chat
        return NotificationResponse.suppress()
    }

    val builder = buildNotificationForChat(message)
    return NotificationResponse.create("chat_tag", 101, builder.build())
}

Java

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ProcessLifecycleOwner;

@Override
public NotificationResponse onNotificationReceived(RemoteMessage message) {
    String chatId = message.getData().get("chat_id");
    boolean isAppForeground = ProcessLifecycleOwner.get()
            .getLifecycle()
            .getCurrentState()
            .isAtLeast(Lifecycle.State.STARTED);

    if (isAppForeground && chatRepository.isConversationActive(chatId)) {
        // Suppress notification display because the user is actively viewing this chat
        return NotificationResponse.suppress();
    }

    NotificationCompat.Builder builder = buildNotificationForChat(message);
    return NotificationResponse.create("chat_tag", 101, builder.build());
}

Thông báo về sự kiện phát trực tiếp hoặc huỷ cảnh báo cũ (FcmNotificationManager)

Nếu bạn cần đăng nhiều thông báo cho một thông báo đẩy đến hoặc huỷ thông báo cũ trước khi hiển thị thông báo mới, hãy sử dụng FcmNotificationManager:

Kotlin

override fun onNotificationReceived(message: RemoteMessage): NotificationResponse {
    // Retrieve the scoped manager for this message execution
    val manager = getFcmNotificationManager(message)

    // 1. Cancel an outdated notification right away
    manager.cancelNow("message_tag", 50)

    // 2. Post the item notification immediately
    val itemNotif = NotificationCompat.Builder(this, "chat_channel")
        .setSmallIcon(R.drawable.ic_message)
        .setContentTitle("Alex")
        .setContentText(message.data["text"])
    manager.postNow("item_tag", 51, itemNotif.build())

    // 3. Post a group summary notification
    val summaryNotif = NotificationCompat.Builder(this, "chat_channel")
        .setSmallIcon(R.drawable.ic_message)
        .setContentTitle("New Conversations")
        .setGroupSummary(true)
    manager.postNow("summary_tag", 52, summaryNotif.build())

    // 4. Return finished() to complete the streaming workflow
    return manager.finished()
}

Java

@Override
public NotificationResponse onNotificationReceived(RemoteMessage message) {
    // Retrieve the scoped manager for this message execution
    FcmNotificationManager manager = getFcmNotificationManager(message);

    // 1. Cancel an outdated notification right away
    manager.cancelNow("message_tag", 50);

    // 2. Post the item notification immediately
    NotificationCompat.Builder itemNotif = new NotificationCompat.Builder(this, "chat_channel")
            .setSmallIcon(R.drawable.ic_message)
            .setContentTitle("Alex")
            .setContentText(message.getData().get("text"));
    manager.postNow("item_tag", 51, itemNotif.build());

    // 3. Post a group summary notification
    NotificationCompat.Builder summaryNotif = new NotificationCompat.Builder(this, "chat_channel")
            .setSmallIcon(R.drawable.ic_message)
            .setContentTitle("New Conversations")
            .setGroupSummary(true);
    manager.postNow("summary_tag", 52, summaryNotif.build());

    // 4. Return finished() to complete the streaming workflow
    return manager.finished();
}

Xử lý thông báo BackgroundSync bằng onBackgroundSyncReceived

Để xử lý tin nhắn BackgroundSync trên Android, hãy sử dụng onBackgroundSyncReceived():

Kotlin

override fun onBackgroundSyncReceived(message: RemoteMessage) {
    val syncCommand = message.data["sync_action"]

    try {
        // Perform local database synchronization or cache refresh
        AppDataRepository.getInstance(applicationContext).syncWithServer(syncCommand)
    } catch (e: Exception) {
        Log.e("FCM", "Background sync operation failed", e)
    }
    // Execution completes -> GMS Core marks the message as handled and unbinds.
}

Java

@Override
public void onBackgroundSyncReceived(RemoteMessage message) {
    String syncCommand = message.getData().get("sync_action");

    try {
        // Perform local database synchronization or cache refresh
        AppDataRepository.getInstance(getApplicationContext()).syncWithServer(syncCommand);
    } catch (Exception e) {
        Log.e("FCM", "Background sync operation failed", e);
    }
    // Execution completes -> GMS Core marks the message as handled and unbinds.
}

Xử lý các yêu cầu xoá tin nhắn đang chờ xử lý bằng onDeletedMessages

Trong một số trường hợp, chẳng hạn như khi thiết bị không kết nối với FCM trong một khoảng thời gian dài hoặc khi hàng đợi của máy chủ đã đầy, FCM có thể xoá các tin nhắn đang chờ xử lý trước khi gửi. Trong những trường hợp này, hãy ghi đè onDeletedMessages():

Kotlin

override fun onDeletedMessages() {
    // Perform a full state synchronization with your app server
    AppDataRepository.getInstance(applicationContext).scheduleFullSync()
}

Java

@Override
public void onDeletedMessages() {
    // Perform a full state synchronization with your app server
    AppDataRepository.getInstance(getApplicationContext()).scheduleFullSync();
}

Di chuyển các ứng dụng cụ thể

Gửi thông báo được mã hoá hai đầu (E2EE)

Khi gửi thông báo được mã hoá hai đầu bằng RemoteNotification, nội dung thông báo đã giải mã sẽ nằm trong ứng dụng của bạn và nền tảng Android:

  1. Gửi thông báo có thể thay đổi: Máy chủ của bạn gửi một RemoteNotification có mutable_content: true, tải trọng được mã hoá trong data và một bản sao lưu chung không được mã hoá notification. GMS Core sẽ gửi thông báo đã mã hoá đến SDK FCM nguồn mở bên trong ứng dụng của bạn.
  2. Giải mã và trả về thông báo: SDK FCM gọi onNotificationReceived(). Ứng dụng của bạn giải mã tải trọng trên thiết bị, tạo Notification Android đã giải mã cuối cùng và chuyển lại cho SDK FCM nguồn mở bằng NotificationResponse hoặc FcmNotificationManager.
  3. Đăng lên nền tảng và báo cáo siêu dữ liệu: SDK nguồn mở FCM đăng thông báo đã giải mã trực tiếp lên API NotificationManager của nền tảng Android và chỉ gửi siêu dữ liệu thông báo (chẳng hạn như mã thông báo và dấu thời gian) trở lại GMS Core để ghi nhận việc phân phối. Nội dung đã giải mã sẽ không bao giờ được gửi đến GMS Core.
  4. Đăng thông báo dự phòng nếu cần: Chỉ khi ứng dụng của bạn không phản hồi trong thời gian chờ 20 giây hoặc vượt quá giới hạn chặn trong nền, thì GMS Core mới đăng trực tiếp notification dự phòng chung lên nền tảng Android.

Sơ đồ cấu trúc cho thấy quy trình phân phối thông báo được mã hoá hai đầu gồm 4 bước giữa GMS Core, SDK FCM nguồn mở, ứng dụng của bạn và API NotificationManager của nền tảng Android.

Tìm hiểu về việc chặn thông báo và thời gian chờ

Để duy trì khả năng phân phối đáng tin cậy trên hệ sinh thái Android, GMS Core áp dụng thời gian chờ xử lý và giới hạn chặn thông báo khi xử lý thông báo RemoteNotification:

Hết thời gian xử lý

Khi mutable_content được đặt thành true, phương thức onNotificationReceived() của bạn phải trả về NotificationResponse trong vòng 20 giây. Nếu quá trình thực thi vượt quá ngưỡng này, lệnh gọi lại sẽ bị huỷ và GMS Core sẽ tự động đăng thông báo dự phòng được hiển thị từ tải trọng remote_notification.notification.

Tỷ lệ chặn thông báo

Vì thông báo RemoteNotification bỏ qua các quy định hạn chế của chế độ Nghỉ để thông báo cho người dùng ngay lập tức và được gửi mà không bị trễ, nên các ứng dụng sẽ hiển thị thông báo cho các thông báo RemoteNotification đã nhận.

Mỗi ứng dụng được phép chặn tối đa 25% lưu lượng truy cập RemoteNotification khi chạy ở chế độ nền. Nếu tỷ lệ chặn của một ứng dụng vượt quá 25%, thì GMS Core sẽ tự động hiển thị và đăng một thông báo dự phòng được hiển thị từ tải trọng remote_notification.notification.

Các hoạt động chặn được thực hiện khi ứng dụng đang hoạt động ở nền trước sẽ không được tính vào ngưỡng này. Các ứng dụng được cấp quyền sử dụng không hạn chế ở chế độ nền cũng được miễn các giới hạn về việc chặn hoạt động trong nền.