Firebase Cloud Messaging を使用してメッセージを受信する

このガイドでは、メッセージを確実に受信できるように、モバイル クライアント アプリとウェブ クライアント アプリで Firebase Cloud Messaging を設定する方法について説明します。

メッセージを受信するには、FirebaseMessagingService を拡張したサービスを使用します。このサービスは onMessageReceived コールバックと onDeletedMessages コールバックをオーバーライドする必要があります。

onMessageReceived は次の例外を除いて、ほとんどのメッセージ タイプに提供されます。

  • アプリがバックグラウンドで動作しているときに配信された通知メッセージ。この場合、通知はデバイスのシステムトレイに配信されます。ユーザーが通知をタップすると、デフォルトでアプリ ランチャーが開きます。

  • バックグラウンドで受信されたときに通知とデータ ペイロードの両方を持つメッセージ。この場合、通知はデバイスのシステムトレイに配信され、データ ペイロードはランチャー アクティビティのインテントの追加部分に配信されます。

要約:

アプリの状態 通知 データ 両方
フォアグラウンド onMessageReceived onMessageReceived onMessageReceived
バックグラウンド システムトレイ onMessageReceived 通知: システムトレイ データ: インテントの追加部分内

メッセージ タイプの詳細については、通知とデータ メッセージをご覧ください。

onMessageReceived コールバックには、通知を送信できるようにするタイムアウトが指定されていますが、このタイマーはアプリがネットワークにアクセスしたり、追加の作業を行ったりすることを想定していません。したがって、アプリでより複雑な処理を行う場合は、アプリが確実に処理を完了できるよう、追加の対応が必要となります。

アプリでのメッセージの処理に 10 秒近くかかる可能性がある場合は、WorkManager ジョブをスケジュール設定するか、下記の WakeLock のガイダンスに沿って対応する必要があります。メッセージを処理するための時間枠は、onMessageReceived の呼び出しに先立って発生する遅延(OS の遅延、アプリの起動時間、他のオペレーションによってブロックされているメインスレッド、または onMessageReceived の呼び出し時間がかかりすぎるなど)に応じて 10 秒より短くなる場合があります。タイマーが切れると、アプリはプロセス強制終了バックグラウンド実行制限の対象となる可能性があります。ネットワーク トランザクションやアプリの起動のレイテンシは大きくなる可能性があるため、ネットワーク アクセスや大量のデータ読み込みの要件といった非同期の依存関係がある場合は、メッセージ処理に時間がかかることを想定して計画を立ててください。

アプリ マニフェストの編集

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 では、以下に対してカスタム デフォルト アイコンが表示され、使用されます。

  • Notifications Composer から送信されたすべての通知メッセージ。
  • 通知ペイロード内にアイコンが明示的に設定されていない通知メッセージ。

カスタム デフォルト アイコンが設定されず、通知ペイロード内にもアイコンが設定されていない場合は、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 ジョブを作成する必要があります。WakeLock は、onMessageReceived のデフォルトのタイムアウトを超える可能性のある短い処理アクティビティに適しています。サーバーに複数のシリアル RPC を送信するといった拡張ワークフローでは、WakeLock よりも WorkManager ジョブを使用する方が適しています。このセクションでは、WakeLock の使用方法に焦点を当てて説明します。WakeLock は、アプリの実行中にデバイスがスリープ状態になるのを防ぎます。これにより、バッテリーの使用量が増える可能性があるため、WakeLock の使用は、メッセージの処理中にアプリを一時停止すべきでない場合に限定する必要があります。たとえば、次のような場合です。

  • 時間的制約のあるユーザーへの通知。
  • デバイス外とのインタラクションで中断すべきでないもの(ネットワーク転送や、ペア設定されたスマートウォッチなどの別のデバイスとの通信など)。

まず、アプリが WakeLock 権限をリクエストしていることを確認する必要があります(FCM SDK にはこれがデフォルトで含まれているため、通常は何も追加する必要はありません)。

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

次に、アプリは、FirebaseMessagingService.onMessageReceived() コールバックの開始時に 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 件を超えている)場合や、デバイスが 1 か月以上 FCM に接続されていない場合に起こります。このような場合、FirebaseMessagingService.onDeletedMessages() へのコールバックを受け取る可能性があります。アプリ インスタンスがこのコールバックを受け取った場合は、アプリサーバーとの完全な同期を実行する必要があります。過去 4 週間以内にそのデバイスのアプリにメッセージを送信していない場合、FCMonDeletedMessages()を呼び出しません。

バックグラウンド アプリの通知メッセージの処理

アプリがバックグラウンドで動作しているとき、Android ではシステムトレイに通知メッセージが送られます。ユーザーが通知をタップすると、デフォルトでアプリ ランチャーが開きます。

通知とデータ ペイロードの両方を含むメッセージ(および Notifications コンソールから送信されたすべてのメッセージ)がここに含まれます。この場合、通知はデバイスのシステムトレイに配信され、データ ペイロードはランチャー アクティビティのインテントの追加部分に配信されます。

アプリへのメッセージ配信については、FCM レポート ダッシュボードをご覧ください。このダッシュボードには、Android アプリの「インプレッション」(ユーザーが表示した通知)のデータとともに、Apple と Android のデバイスで送信および開封されたメッセージの数が記録されています。