複数のデバイスにメッセージを送信する

Firebase Cloud Messaging では、複数のデバイスをメッセージの対象にする方法が 2 つあります。

このチュートリアルでは、アプリサーバーから HTTP または XMPP プロトコルを用いて FCM 向けにトピック メッセージを送信し、それらのメッセージを Android アプリで受信して処理する手順に重点を置いています。バックグラウンドとフォアグラウンドの両方のアプリについて、メッセージの処理を説明します。セットアップから検証まで、これを実現するためのすべての手順を説明します。

SDK を設定する

FCM 用の Android クライアント アプリの設定や、最初のメッセージを送信する手順をすでに行った場合は、このセクションに記載されている手順を完了している可能性があります。

前提条件

  • 以下を搭載しているデバイス:
    • Android 4.1(API レベル 16、Jelly Bean)以降
    • Google Play 開発者サービス 15.0.0 以降
  • Android Studio の最新バージョン

Android Studio プロジェクトをまだ用意していない場合に Firebase 機能を試すだけであれば、クイックスタート サンプルをダウンロードしてご利用いただけます。クイックスタートを使用する場合は、プロジェクトのモジュール フォルダ(通常は app/)内の build.gradle ファイルからアプリケーション ID を取得してください。このパッケージ名は次のステップで必要になります。

アプリに Firebase を追加する

Firebase をアプリに追加するには、Firebase プロジェクトと、アプリ用の Firebase 構成ファイルが必要です。

Firebase プロジェクトを作成するには:

  1. Firebase コンソールに移動します。

  2. [プロジェクトを追加] をクリックし、[プロジェクト名] でプロジェクト名を選択するか、新しいプロジェクト名を入力します。

    • アプリに関連付けられた既存の Google プロジェクトがある場合は、[プロジェクト名] プルダウン メニューからプロジェクトを選択します。
    • 既存の Google プロジェクトがない場合は、[プロジェクト名] に新しいプロジェクト名を入力します。
  3. (省略可)プロジェクト ID を編集します。

    Firebase プロジェクトには一意の ID が自動的に割り当てられます。一般公開される Firebase サービスでは、この ID は次のように表示されます。

    • デフォルト Realtime Database URL - your-project-id.firebaseio.com
    • デフォルト Cloud Storage バケット名 - your-project-id.appspot.com
    • デフォルト ホスティング サブドメイン - your-project-id.firebaseapp.com
  4. Firebase コンソールで残りの設定手順を実施した後、[プロジェクトを作成](既存の Google プロジェクトを使用する場合は [Firebase を追加])をクリックします。

Firebase プロジェクトのリソースが自動的にプロビジョニングされます。通常、この処理には数分かかります。処理が完了すると、Firebase コンソールで Firebase プロジェクトの概要ページが表示されます。

これでプロジェクトを用意できました。プロジェクトに Android アプリを追加できます。

  1. [Android アプリに Firebase を追加] をクリックし、設定手順に沿って操作します。既存の Google プロジェクトをインポートする場合、このステップは自動的に実行されることがあります。その場合は、構成ファイルをダウンロードするだけでかまいません。

  2. メッセージが表示されたら、アプリのパッケージ名を入力します。必ずアプリで使用しているパッケージ名を入力してください。パッケージ名を設定できるのは、アプリを Firebase プロジェクトに追加するときだけです。

  3. Firebase Android 構成ファイルをアプリに追加します。

    1. [google-services.json をダウンロード] をクリックして、Firebase Android 構成ファイル(google-services.json)を入手します。

      Firebase Android 構成ファイルはいつでも再ダウンロードできます。

    2. 構成ファイルをルートレベルの build.gradle ファイルと同じディレクトリに移動します。

  4. 初期化コードを追加したらアプリを実行して、Firebase を正常にインストールしたという確認を Firebase コンソールに送信します。

SDK を追加する

Firebase ライブラリをプロジェクトに統合する場合は、Android Studio プロジェクトを準備するためのいくつかの基本タスクを実行する必要があります。ただし、アプリに Firebase を追加するときに、すでにこの手順が完了している可能性があります。

まず、ルートレベルの build.gradle ファイルにビルドルールを追加し、google-services プラグインと Google の Maven リポジトリを含めます。

buildscript {
    // ...
    dependencies {
        // ...
        classpath 'com.google.gms:google-services:4.2.0' // google-services plugin
    }
}

allprojects {
    // ...
    repositories {
        google() // Google's Maven repository
        // ...
    }
}

次に、モジュールの Gradle ファイル(通常は app/build.gradle)の末尾に apply plugin 行を追加して、Gradle プラグインを有効にします。

apply plugin: 'com.android.application'

android {
  // ...
}

dependencies {
  // ...
  implementation 'com.google.firebase:firebase-core:16.0.7'
  implementation 'com.google.firebase:firebase-messaging:17.3.4'
  // Getting a "Could not find" error? Make sure you have
  // added the Google maven respository to your root build.gradle
}

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

また、使用する Firebase SDK の依存関係も追加する必要があります。まず、Firebase 向け Google アナリティクス機能を提供する com.google.firebase:firebase-core から始めることをおすすめします。使用可能なライブラリの一覧をご覧ください。

クライアント アプリをトピックに登録する

クライアント アプリを既存のトピックに登録することも、新しいトピックを作成することもできます。クライアント アプリを新しいトピック名(Firebase プロジェクトにまだ存在していないトピック名)に登録すると、その名前の新しいトピックが FCM に作成され、その後すべてのクライアントはそのトピック名に登録できるようになります。

トピックに登録する場合、クライアント アプリは、FCM トピック名を指定して Firebase Cloud Messaging subscribeToTopic() を呼び出します。このメソッドは Task を返します。この戻り値により、完了リスナーは登録が正常に完了したかどうかを判断できます。

Java
Android

FirebaseMessaging.getInstance().subscribeToTopic("weather")
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                String msg = getString(R.string.msg_subscribed);
                if (!task.isSuccessful()) {
                    msg = getString(R.string.msg_subscribe_failed);
                }
                Log.d(TAG, msg);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });

Kotlin
Android

FirebaseMessaging.getInstance().subscribeToTopic("weather")
        .addOnCompleteListener { task ->
            var msg = getString(R.string.msg_subscribed)
            if (!task.isSuccessful) {
                msg = getString(R.string.msg_subscribe_failed)
            }
            Log.d(TAG, msg)
            Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
        }

登録解除する場合、クライアント アプリはトピック名で Firebase Cloud Messaging unsubscribeFromTopic() を呼び出します。

メッセージを受信して処理する

FCM は、他のダウンストリーム メッセージと同じようにトピック メッセージを配信します。

メッセージを受信するには、FirebaseMessagingService を拡張したサービスを使用します。このサービスは onMessageReceived コールバックと onDeletedMessages コールバックをオーバーライドする必要があります。メッセージは受信から 20 秒以内に処理されるようにします(Android Marshmallow では 10 秒)。時間ウィンドウは、onMessageReceived を呼び出す前に発生した OS の遅延に応じて短くなる場合があります。この時間が経過すると、Android O のバックグラウンド実行制限などの OS の各種動作によって、作業完了が妨げられる可能性があります。詳細については、メッセージの優先度の概要をご覧ください。

onMessageReceived はほとんどのメッセージ タイプに用意されていますが、次の例外があります。

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

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

要約:

アプリの状態 通知 データ ペイロード 両方
フォアグラウンド onMessageReceived onMessageReceived onMessageReceived
バックグラウンド システムトレイ onMessageReceived 通知: システムトレイ
インテントの追加部分にあるデータ
メッセージ タイプについて詳しくは、通知とデータ メッセージをご覧ください。

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

FirebaseMessagingService を使用するには、アプリのマニフェストに次の設定を追加する必要があります。

<service android:name=".java.MyFirebaseMessagingService">
    <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 では、以下の場合にカスタム デフォルト カラーが使用されます。

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

カスタム デフォルト アイコンが設定されず、通知ペイロード内にもアイコンが設定されていない場合は、Android では白色でレンダリングされたアプリケーション アイコンが表示されます。

onMessageReceived のオーバーライド

FirebaseMessagingService.onMessageReceived メソッドをオーバーライドすると、受信した RemoteMessage オブジェクトに基づいて操作を行い、メッセージ データを取得できます。

Java
Android

@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 Firebase Job Dispatcher.
            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.
}

Kotlin
Android

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.
    remoteMessage?.data?.isNotEmpty()?.let {
        Log.d(TAG, "Message data payload: " + remoteMessage.data)

        if (/* Check if data needs to be processed by long running job */ true) {
            // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
            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.
}

onDeletedMessages のオーバーライド

場合によっては、FCM からメッセージが配信されないことがあります。これは、特定のデバイスが接続されたときにそのデバイスにまだ配信されていないアプリのメッセージが多すぎる(100 件を超えている)場合や、デバイスが 1 か月以上 FCM に接続されていない場合に起こります。このような場合、FirebaseMessagingService.onDeletedMessages() へのコールバックを受け取ることがあります。アプリ インスタンスがこのコールバックを受け取った場合は、アプリサーバーとの完全な同期を実行する必要があります。過去 4 週間以内にそのデバイスのアプリにメッセージを送信していない場合、onDeletedMessages() は呼び出されません。

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

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

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

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

バックグラウンドでの使用が制限されたアプリ(Android P 以降)

2019 年 1 月からは、ユーザーが([設定] -> [アプリと通知] -> [appname] -> [電池] などから)バックグラウンドでの使用を制限したアプリに FCM のメッセージが配信されなくなります。バックグラウンドでの使用の制限が解除されると、アプリへの新しいメッセージが以前のように配信されます。バックグラウンドでの使用が制限されると、メッセージが失われるなど、さまざまな影響が生じるため、Android Vitals に表示される不適切な動作を避けるようにしてください。これらの動作は、Android デバイスがアプリのバックグラウンドでの使用を制限するようユーザーに推奨する原因になります。アプリのバックグラウンドでの使用が制限されているかどうかを確認するには、isBackgroundRestricted() を使用します。

送信リクエストを作成する

Firebase Cloud Messaging トピックへのメッセージを送信する方法は、個々のデバイスやユーザー グループ宛てのメッセージを送信する場合とよく似ています。アプリサーバーは to キーに /topics/yourTopic などの値を設定します。デベロッパーは、正規表現 "/topics/[a-zA-Z0-9-_.~%]+" に一致するすべてのトピック名を選択できます。

複数のトピックの組み合わせに送信する場合、アプリサーバーは(to キーではなく)condition キーで、対象のトピックを指定するブール条件を設定する必要があります。たとえば、TopicA と、TopicB または TopicC のいずれかに登録されたデバイスにメッセージを送信する場合には、次のようになります。

'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)

FCM はまず、かっこ内の条件を評価し、次に左から右に式を評価していきます。上記の式では、いずれか 1 つのトピックだけに登録したユーザーはメッセージを受信しません。同様に、TopicA に登録していないユーザーはメッセージを受信しません。メッセージを受信するのは、次の組み合わせの場合のみです。

  • TopicA と TopicB
  • TopicA と TopicC

条件式には最大 5 つのトピックを含めることができ、かっこを使用できます。使用できる演算子は &&||! です。! の使用方法に注意してください。

!('TopicA' in topics)

この式では、どのトピックにも登録されていないアプリ インスタンスを含め、TopicA に登録されていないアプリ インスタンスすべてがメッセージを受信します。

アプリサーバーのキーの詳細については、選択した接続サーバー プロトコル(HTTP または XMPP)のリファレンス情報をご覧ください。このページでは、メッセージをトピックに送信する方法として HTTP と XMPP の両方の例を示しています。

トピック HTTP POST リクエスト

単一のトピックに送信します。

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "to": "/topics/foo-bar",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

「犬」('dogs')または「猫」('cats')のトピックに登録されたデバイスに送信します。

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA
{
  "condition": "'dogs' in topics || 'cats' in topics",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

トピック HTTP レスポンス

//Success example:
{
  "message_id": "1023456"
}

//failure example:
{
  "error": "TopicsMessageRateExceeded"
}

トピック XMPP メッセージ

単一のトピックに送信します。

<message id="">
  <gcm xmlns="google:mobile:data">
{
  "to": "/topics/foo-bar",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

  </gcm>
</message>

「犬」('dogs')または「猫」('cats')のトピックに登録されたデバイスに送信します。

<message id="">
  <gcm xmlns="google:mobile:data">
{
  "condition": "'dogs' in topics || 'cats' in topics",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}

  </gcm>
</message>

トピック XMPP レスポンス

//Success example:
{
  "message_id": "1023456"
}

//failure example:
{
  "error": "TopicsMessageRateExceeded"
}

FCM サーバーがトピック送信リクエストに成功または失敗のレスポンスを返すまでに、最大 30 秒の遅延が発生する可能性があります。それに応じて、リクエスト内でアプリサーバーのタイムアウト値を必ず設定してください。

メッセージ オプションの全一覧については、選択した接続サーバー プロトコル(HTTP または XMPP)のリファレンス情報をご覧ください。

次のステップ

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。