Migracja z interfejsu FCM HTTP v1 API na AndroidConfigV2

Za pomocą FCM AndroidConfigV2 możesz wysyłać wiadomości oparte na intencjach na urządzenia z Androidem, korzystając z uproszczonego interfejsu API i zapewniając terminowe dostarczanie powiadomień.

Z tego przewodnika dowiesz się, jak przenieść żądania wysyłania z backendu z AndroidConfig do AndroidConfigV2 oraz jak zaktualizować klientów aplikacji na Androida ze starszej obsługi onMessageReceived do nowych wywołań zwrotnych klienta w wersji 2 onNotificationReceived i onBackgroundSyncReceived.

Nowości w AndroidConfigV2

Interfejs FCM AndroidConfigV2 API upraszcza pracę deweloperów, wprowadzając 2 typy wiadomości:

  • RemoteNotification: zalecane w przypadku wszystkich wiadomości FCM, które wyświetlają pilne, ważne powiadomienie widoczne dla użytkownika. Te wiadomości omijają wszelkie środki oszczędzania baterii i są dostarczane bez opóźnień. Na urządzeniu pakiet SDK FCM i usługi Google Play przejmują kontrolę nad publikowaniem powiadomień w NotificationManager Androida.
  • BackgroundSync: wiadomości z danymi, które nie są pilne, przeznaczone do synchronizacji w tle i aktualizacji stanu aplikacji. Dostarczanie może być grupowane lub opóźniane, aby zoptymalizować czas pracy na baterii.
Funkcja Starsza wersja (AndroidConfig v1) Nowe (AndroidConfigV2)
Pole konfiguracji message.android message.android_v2
Definicja priorytetu Wyraźny priority: "high" lub priority: "normal" Wynika z typu unii (remote_notification w przypadku pilnych alertów i background_sync w przypadku synchronizacji, która nie jest pilna)
Publikowanie powiadomień Aplikacja ręcznie wywołuje NotificationManager.notify() Pakiet SDK FCM i usługi GMS Core publikują dane bezpośrednio lub za pomocą NotificationResponse
Wywołania zwrotne klienta • onMessageReceived(RemoteMessage)
• onDeletedMessages()
• onNotificationReceived(RemoteMessage)
• onBackgroundSyncReceived(RemoteMessage)
• onDeletedMessages()
Możliwość zwijania Różne; wymagane jest jawne ustawienie Oba typy wiadomości mogą być zwijane lub nie (domyślnie nie są zwijane).

Autoryzowanie żądań wysyłania AndroidConfigV2

Możesz nadal używać tokenów dostępu OAuth 2.0 pochodzących z konta usługi Firebase za pomocą domyślnego uwierzytelniania aplikacji Google lub pakietu Admin SDK i wysyłać żądania POST do punktu końcowego HTTP v1 messages:send.

Jeśli nie używasz konta usługi Firebase, pamiętaj, aby przypisać swoje dedykowane konto do roli Administrator interfejsu Komunikacji w chmurze Firebase (FCM) API (roles/firebase.messagingAdmin) i poprosić o zakres protokołu OAuth 2.0 https://www.googleapis.com/auth/firebase.messaging podczas generowania tokenów dostępu:

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

Aktualizowanie żądań wysyłania serwera (REST)

Aby wysłać żądanie AndroidConfigV2 za pomocą interfejsu API REST, zastąp obiekt android w treści żądania obiektem android_v2. W obiekcie android_v2 określ parametry wspólne (takie jak ttl, collapse_key, data i restricted_package_name) oraz podaj jedno z tych pól unii: remote_notification lub background_sync.

Wysyłanie wiadomości RemoteNotification

Aby wysłać pilne powiadomienie dla użytkownika, dołącz obiekt remote_notification i zalecane dane notification. Domyślnie parametr mutable_content ma wartość „false”. GMS Core natychmiast renderuje i publikuje powiadomienie w obszarze powiadomień bez wykonywania kodu aplikacji działającej w tle.

Przykładowy kod JSON pełnego żądania HTTP (powiadomienie renderowane automatycznie)

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
        }
      }
    }
  }
}

Wysyłanie powiadomienia RemoteNotification z treścią, którą można modyfikować

Jeśli aplikacja musi zmodyfikować powiadomienie na urządzeniu, zanim zostanie ono wyświetlone, ustaw mutable_content: true wewnątrz remote_notification. Spowoduje to, że GMS Core połączy się z aplikacją kliencką i wywoła onNotificationReceived(). Jest to przydatne, gdy chcesz odszyfrować ładunek zaszyfrowany metodą end-to-end, dołączyć awatary użytkowników lub zlokalizować ciągi znaków.

Pamiętaj, aby uwzględnić obiekt notification w obiekcie remote_notification, nawet gdy mutable_content ma wartość true. GMS Core używa tego obiektu notification jako powiadomienia zastępczego, jeśli wywołanie zwrotne klienta przekroczy limit czasu lub limit blokowania.

Jeśli przeprowadzasz migrację ze starszych wiadomości z danymi w wersji 1 i chcesz, aby usługa FCM dostarczała ładunek do starszych instalacji klienta w wersji 1 jako czystą wiadomość z danymi, ustaw use_as_v1_data_message: true w remote_notification.

Przykładowy kod JSON pełnego żądania 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
        }
      }
    }
  }
}

Wyślij wiadomość BackgroundSync

Aby wysłać wiadomość, która nie jest pilna, na potrzeby synchronizacji danych w tle, aktualizacji stanu wyciszenia lub aktualizacji plakietki aplikacji bądź widżetu, użyj obiektu background_sync. Nie używaj tych wiadomości do publikowania powiadomień widocznych dla użytkownika, ponieważ mogą one być grupowane lub opóźniane w celu optymalizacji czasu pracy na baterii.

Przykładowy kod JSON pełnego żądania 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": {}
    }
  }
}

Aktualizowanie wywołań zwrotnych klienta Androida

Aby otrzymywać wiadomości AndroidConfigV2 na Androidzie, zaktualizuj aplikację, aby korzystała z najnowszego pakietu SDK firebase-messaging. Zaktualizuj usługę rozszerzającą FirebaseMessagingService, implementując onNotificationReceived() i onBackgroundSyncReceived().

Instalowanie pakietu SDK EAP

W ramach programu wcześniejszego dostępu pakiet SDK firebase-messaging w wersji 2 jest rozpowszechniany jako lokalne archiwum repozytorium Maven, a nie przez publiczne repozytorium Maven Google.

  1. Dodaj katalog m2repository zawierający plik firebase-messaging:26.1.0-eap01 do katalogu głównego projektu aplikacji na Androida (<project-root>/m2repository), korzystając z jednej z tych opcji:

    1. Opcja A. Pobierz gotowe archiwum: pobierz plik FirebaseMessaging_26.1.0-eap01.zip i wyodrębnij katalog m2repository do katalogu głównego projektu na Androida (<project-root>/m2repository).
    2. Opcja B. Kompilowanie z GitHuba za pomocą Gradle: sklonuj gałąź feature/fcm-android-config-v2-eap repozytorium firebase/firebase-android-sdk, skompiluj lokalne repozytorium Maven i skopiuj wygenerowany katalog build/m2repository do katalogu głównego projektu aplikacji na Androida:

      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. W pliku settings.gradle.kts (lub settings.gradle) na poziomie projektu dodaj lokalny katalog m2repository na początku bloku repositories, nad 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. W pliku app/build.gradle.kts (lub app/build.gradle) na poziomie modułu zaktualizuj zależność firebase-messaging do wersji 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. Zsynchronizuj projekt z plikami Gradle.

Obsługa RemoteNotifications za pomocą onNotificationReceived

Jeśli zasada AndroidConfigV2 jest ustawiona na RemoteNotification, komunikaty są wyświetlane, chyba że zasada mutable_content ma wartość Prawda lub komunikaty są wyciszone w zasadzie onNotificationReceived().

Gdy RemoteNotification dotrze z mutable_content: true, GMS Core wiąże się z Twoją aplikacją i wywołuje onNotificationReceived(). Metoda musi zwrócić NotificationResponse w ciągu 20 sekund.

Zwracanie zmodyfikowanego powiadomienia

Obiekt Notification przekazywany do metody NotificationResponse.create zastępuje właściwości zastępczych danych serwera remote_notification.notification zdefiniowane w żądaniu wysłania. Aby zachować wartości z żądania wysłania, odczytaj je z message.notification (message.getNotification() w języku Java) podczas stosowania modyfikacji na urządzeniu:

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());
    }
}

Ukrywanie powiadomienia

Jeśli użytkownik ma obecnie aktywny odpowiedni ekran lub czat, możesz pominąć powiadomienie, zwracając 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());
}

Powiadomienia o transmisji lub anulowanie starszych alertów (FcmNotificationManager)

Jeśli chcesz opublikować kilka powiadomień dotyczących jednego przychodzącego powiadomienia push lub anulować nieaktualne powiadomienie przed wyświetleniem nowego, użyj 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();
}

Obsługuj wiadomości BackgroundSync za pomocą onBackgroundSyncReceived

Aby przetwarzać wiadomości BackgroundSync na Androidzie, użyj 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.
}

Obsługuj oczekujące usunięcia wiadomości za pomocą onDeletedMessages

W niektórych sytuacjach, na przykład gdy urządzenie nie połączyło się z FCM przez dłuższy czas lub gdy kolejka serwera jest pełna, FCM może usunąć oczekujące wiadomości przed ich dostarczeniem. W takich przypadkach zastąp 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();
}

Migracja określonych aplikacji

wysyłanie powiadomień z pełnym szyfrowaniem (E2EE);

Podczas dostarczania powiadomienia z pełnym szyfrowaniem za pomocą RemoteNotification odszyfrowana treść powiadomienia pozostaje w aplikacji i na platformie Androida:

  1. Dostarczanie powiadomienia, które można zmienić: Twój serwer wysyła RemoteNotification z mutable_content: true, zaszyfrowane dane w data i ogólną niezaszyfrowaną kopię zapasową notification. GMS Core dostarcza zaszyfrowaną wiadomość do pakietu SDK FCM typu open source w Twojej aplikacji.
  2. Odszyfrowanie i powiadomienie o zwrocie: pakiet SDK FCM wywołuje onNotificationReceived(). Aplikacja odszyfrowuje ładunek lokalnie, tworzy ostateczny odszyfrowany obiekt Notification i przekazuje go z powrotem do pakietu SDK FCM o otwartym kodzie źródłowym za pomocą NotificationResponse lub FcmNotificationManager.
  3. Publikowanie na platformie i raportowanie metadanych: pakiet SDK FCM o otwartym kodzie źródłowym publikuje odszyfrowane powiadomienie bezpośrednio w interfejsie API NotificationManager platformy Androida i wysyła do GMS Core tylko metadane powiadomienia, takie jak identyfikatory powiadomień i sygnatury czasowe, aby umożliwić obserwowanie dostarczania. Odszyfrowane treści nigdy nie są wysyłane do GMS Core.
  4. W razie potrzeby opublikuj powiadomienie zapasowe: tylko wtedy, gdy aplikacja nie odpowie w ciągu 20 sekund lub przekroczy limity wyciszania w tle, GMS Core opublikuje ogólne powiadomienie zapasowe notification bezpośrednio na platformie Androida.

Schemat architektury przedstawiający 4-etapowy proces dostarczania powiadomień z pełnym szyfrowaniem między GMS Core, pakietem SDK FCM typu open source, Twoją aplikacją i interfejsem API NotificationManager platformy Androida.

Omówienie blokowania powiadomień i czasu oczekiwania

Aby zapewnić niezawodne dostarczanie wiadomości w ekosystemie Androida, GMS Core stosuje limity czasu przetwarzania i limity blokowania powiadomień podczas obsługi wiadomości RemoteNotification:

Przekroczenie limitu czasu przetwarzania

Gdy właściwość mutable_content jest ustawiona na true, metoda onNotificationReceived() musi zwrócić NotificationResponse w ciągu 20 sekund. Jeśli czas wykonywania przekroczy ten próg, wywołanie zwrotne zostanie anulowane, a usługi GMS automatycznie opublikują powiadomienie zastępcze wygenerowane na podstawie danych remote_notification.notification.

Współczynnik pomijania powiadomień

Wiadomości RemoteNotification omijają ograniczenia trybu Doze, aby natychmiast powiadomić użytkowników, i są dostarczane bez opóźnień, dlatego aplikacje powinny wyświetlać powiadomienia o odebranych wiadomościach RemoteNotification.

Każda aplikacja może pominąć do 25% ruchu RemoteNotification, gdy działa w tle. Jeśli współczynnik pomijania aplikacji przekracza 25%, GMS Core automatycznie renderuje i publikuje powiadomienie zastępcze wyrenderowane z ładunku remote_notification.notification.

Blokady wykonywane, gdy aplikacja jest aktywna na pierwszym planie, nie wliczają się do tego progu. Aplikacje, którym przyznano uprawnienia do nieograniczonego użycia w tle, są również zwolnione z limitów dotyczących blokowania w tle.