FCM HTTP v1 API から AndroidConfigV2 に移行する

FCM AndroidConfigV2 を使用すると、タイムリーな通知配信と簡素化された API を使用して、インテント駆動型のメッセージを Android デバイスに送信できます。

このガイドでは、バックエンドの送信リクエストを AndroidConfig から AndroidConfigV2 に移行する方法と、Android アプリケーション クライアントを従来の onMessageReceived 処理から新しい v2 クライアント コールバック onNotificationReceived と onBackgroundSyncReceived に更新する方法について説明します。

AndroidConfigV2 の新機能

FCM AndroidConfigV2 API では、次の 2 つのメッセージ タイプが導入され、デベロッパー エクスペリエンスが簡素化されています。

  • RemoteNotification: 緊急性が高く、時間制限のあるユーザーに表示される通知を表示するすべての FCM メッセージに推奨されます。これらのメッセージはバッテリー節約対策をバイパスし、遅延なく配信されます。デバイスでは、FCM SDK と Google Play 開発者サービスが、Android の NotificationManager への通知の投稿を管理します。
  • BackgroundSync: バックグラウンド同期とアプリの状態の更新を目的とした、緊急性のないデータ メッセージ。バッテリー駆動時間を最適化するために、配信をバッチ処理したり、遅らせたりできます。
機能 レガシー(AndroidConfig v1) 新規(AndroidConfigV2)
構成フィールド message.android message.android_v2
候補の定義 明示的な priority: "high" または priority: "normal" ユニオン型によって暗黙的に指定(緊急アラートの場合は remote_notification、緊急でない同期の場合は background_sync)
通知の投稿 アプリが手動で NotificationManager.notify() を呼び出す FCM SDK と GMS Core が直接または NotificationResponse を使用して投稿
クライアント コールバック • onMessageReceived(RemoteMessage)
• onDeletedMessages()
• onNotificationReceived(RemoteMessage)
• onBackgroundSyncReceived(RemoteMessage)
• onDeletedMessages()
折りたたみ可能 さまざま。明示的な設定が必要 どちらのメッセージ タイプも、折りたたみ可能または折りたたみ不可(デフォルトでは折りたたみ不可)にすることができます。

AndroidConfigV2 の送信リクエストを承認する

Google アプリケーションのデフォルト認証情報または Admin SDK を使用して、Firebase サービス アカウントから派生した OAuth 2.0 アクセス トークンを引き続き使用し、POST リクエストを HTTP v1 messages:send エンドポイントに送信できます。

Firebase サービス アカウントを使用していない場合は、専用アカウントを Firebase Cloud Messaging API 管理者ロール(roles/firebase.messagingAdmin)に割り当て、アクセス トークンを生成するときに https://www.googleapis.com/auth/firebase.messaging OAuth 2.0 スコープをリクエストします。

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

サーバー送信リクエストを更新する(REST)

REST API を使用して AndroidConfigV2 リクエストを送信するには、リクエスト本文の android オブジェクトを android_v2 に置き換えます。android_v2 内で、共通パラメータ(ttl、collapse_key、data、restricted_package_name など)を指定し、remote_notification または background_sync のいずれかのユニオン フィールドを指定します。

RemoteNotification メッセージを送信する

ユーザー向けの緊急通知を送信するには、remote_notification オブジェクトと、推奨される notification ペイロードを含めます。デフォルトでは、mutable_content は false に設定されています。GMS Core は、バックグラウンド アプリコードを実行せずに、通知をすぐにレンダリングしてシステムトレイに投稿します。

HTTP リクエストの完全な JSON サンプル(自動レンダリング通知)

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

変更可能なコンテンツを含む RemoteNotification を送信する

アプリでデバイス上の通知を表示前に変更する必要がある場合は、remote_notification 内に mutable_content: true を設定します。これにより、GMS Core はクライアント アプリケーションにバインドし、onNotificationReceived() を呼び出します。これは、エンドツーエンドの暗号化されたペイロードを復号したり、ユーザーのアバターを添付したり、文字列をローカライズしたりする場合に便利です。

mutable_content が true の場合でも、remote_notification 内に notification オブジェクトを含めるようにしてください。GMS Core は、クライアント コールバックがタイムアウトするか、抑制制限を超えた場合、この notification オブジェクトをフォールバック通知として使用します。

以前の v1 データ メッセージから移行し、FCM でペイロードを純粋なデータ メッセージとして古い v1 クライアント インストールに配信する場合は、remote_notification 内に use_as_v1_data_message: true を設定します。

HTTP リクエストの完全な JSON サンプル

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

BackgroundSync メッセージを送信する

バックグラウンド データ同期、サイレント状態の更新、アプリバッジまたはウィジェットの更新のために緊急性の低いメッセージを送信するには、background_sync オブジェクトを使用します。これらのメッセージは、デバイスのバッテリー駆動時間を最適化するためにバッチ処理されたり遅延されたりする可能性があるため、ユーザーに表示される通知の投稿には使用しないでください。

HTTP リクエストの完全な JSON サンプル

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": {}
    }
  }
}

Android クライアントのコールバックを更新する

Android で AndroidConfigV2 メッセージを受信するには、最新の firebase-messaging SDK を使用するようにアプリをアップグレードします。onNotificationReceived() と onBackgroundSyncReceived() を実装して、FirebaseMessagingService を拡張するサービスを更新します。

EAP SDK をインストールする

EAP 期間中、firebase-messaging v2 SDK は Google の公開 Maven リポジトリではなく、ローカルの Maven リポジトリ アーカイブとして配布されます。

  1. 次のいずれかの方法で、firebase-messaging:26.1.0-eap01 を含む m2repository ディレクトリを Android プロジェクトのルート ディレクトリ(<project-root>/m2repository)に追加します。

    1. オプション A: ビルド済みのアーカイブをダウンロードする: FirebaseMessaging_26.1.0-eap01.zip をダウンロードし、m2repository ディレクトリを Android プロジェクトのルート ディレクトリ(<project-root>/m2repository)に抽出します。
    2. オプション B: Gradle を使用して GitHub からビルドする: firebase/firebase-android-sdk の feature/fcm-android-config-v2-eap ブランチのクローンを作成し、ローカルの Maven リポジトリをビルドして、生成された build/m2repository ディレクトリを 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. プロジェクト レベルの settings.gradle.kts(または settings.gradle)で、ローカルの m2repository ディレクトリを repositories ブロックの先頭(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. モジュール レベルの app/build.gradle.kts(または app/build.gradle)で、firebase-messaging 依存関係を 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. プロジェクトを Gradle ファイルと同期します。

onNotificationReceived で RemoteNotifications を処理する

AndroidConfigV2 を指定すると、mutable_content が true に設定されているか、onNotificationReceived() で明示的に無効にされていない限り、RemoteNotification メッセージが表示されます。

mutable_content: true を含む RemoteNotification が到着すると、GMS Core はアプリにバインドして onNotificationReceived() を呼び出します。メソッドは 20 秒以内に NotificationResponse を返す必要があります。

変更された通知を返す

NotificationResponse.create に渡す Notification オブジェクトは、送信リクエストで定義されたサーバーの remote_notification.notification フォールバック ペイロード プロパティを置き換えます。送信リクエストの値を保持するには、デバイス上の変更を適用しながら、message.notification(Java の場合は message.getNotification())から値を読み取ります。

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

通知を抑制する

ユーザーが関連する画面またはチャットを現在アクティブにしている場合は、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());
}

通知をストリーミングするか、古いアラートをキャンセルします(FcmNotificationManager)

1 つの受信プッシュに対して複数の通知を投稿する必要がある場合や、新しい通知を表示する前に古い通知をキャンセルする必要がある場合は、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();
}

onBackgroundSyncReceived を使用して BackgroundSync メッセージを処理する

Android で BackgroundSync メッセージを処理するには、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.
}

onDeletedMessages を使用して保留中のメッセージの削除を処理する

デバイスが長期間 FCM に接続されていない場合や、サーバーキューがいっぱいになっている場合など、状況によっては、FCM が配信前に保留中のメッセージを削除することがあります。このような場合は、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();
}

特定のアプリを移行する

エンドツーエンドの暗号化(E2EE)通知を配信する

RemoteNotification を使用してエンドツーエンドの暗号化された通知を配信する場合、復号された通知コンテンツはアプリと Android プラットフォーム内に留まります。

  1. 変更可能な通知を配信する: サーバーは、mutable_content: true、data の暗号化されたペイロード、一般的な暗号化されていないバックアップ notification を含む RemoteNotification を送信します。GMS Core は、暗号化されたメッセージをアプリ内のオープンソースの FCM SDK に配信します。
  2. 復号して通知を返す: FCM SDK が onNotificationReceived() を呼び出します。アプリはペイロードをローカルで復号し、復号された最終的な Android Notification をビルドして、NotificationResponse または FcmNotificationManager を使用してオープンソースの FCM SDK に返します。
  3. プラットフォームに投稿してメタデータを報告する: オープンソースの FCM SDK は、復号された通知を Android プラットフォームの NotificationManager API に直接投稿し、通知 ID やタイムスタンプなどの通知メタデータのみを GMS Core に返して、配信のオブザーバビリティを確保します。復号されたコンテンツが GMS Core に送信されることはありません。
  4. 必要に応じてバックアップ通知を投稿する: アプリが 20 秒のタイムアウト内に応答しない場合、またはバックグラウンド抑制の上限を超えた場合にのみ、GMS Core は一般的なバックアップ notification を Android プラットフォームに直接投稿します。

GMS Core、オープンソースの FCM SDK、アプリ、Android プラットフォームの NotificationManager API の間で、エンドツーエンドで暗号化された通知を配信する 4 ステップのフローを示すアーキテクチャ図。

通知の抑制とタイムアウトについて

Android エコシステム全体で信頼性の高い配信を維持するため、GMS Core は RemoteNotification メッセージを処理する際に処理タイムアウトと通知抑制制限を適用します。

処理のタイムアウト

mutable_content が true に設定されている場合、onNotificationReceived() メソッドは 20 秒以内に NotificationResponse を返す必要があります。実行がこのしきい値を超えると、コールバックはキャンセルされ、GMS Core は remote_notification.notification ペイロードからレンダリングされたフォールバック通知を自動的に投稿します。

通知の抑制率

RemoteNotification メッセージは、Doze の制限を回避してユーザーにすぐに通知し、遅延なく配信されるため、アプリケーションは受信した RemoteNotification メッセージの通知を表示することが想定されています。

すべてのアプリケーションは、バックグラウンドで実行中に RemoteNotification トラフィックの最大 25% を抑制できます。アプリの抑制率が 25% を超えると、GMS Core は remote_notification.notification ペイロードからレンダリングされたフォールバック通知を自動的にレンダリングして投稿します。

アプリがフォアグラウンドでアクティブなときに実行される抑制は、このしきい値にカウントされません。無制限のバックグラウンド使用権限が付与されているアプリケーションも、バックグラウンド抑制制限の対象外となります。