メッセージを複数のデバイスに送信するには、トピック メッセージングを使用します。この機能を使用すると、特定のトピックを選択した複数のデバイスにメッセージを送信できます。
このチュートリアルでは、Admin SDK または REST API を使用してアプリサーバーから FCM にトピック メッセージを送信し、それらのメッセージを Android アプリで受信して処理する手順を取り上げます。バックグラウンドとフォアグラウンドの両方のアプリのメッセージ処理について、設定から検証まですべての手順を説明します。
SDK を設定する
Android クライアント アプリでの FCM の設定または最初のメッセージを送信する手順がすでに済んでいる場合は、このセクションの一部の手順を省略できます。
始める前に
Android Studio の最新バージョンをインストールするか、更新します。
プロジェクトが次の要件を満たしていることを確認します(プロダクトによっては、より厳しい要件がある場合があります)。
- API レベル 21(Lollipop)以降を対象としていること
- Android 5.0 以上を使用していること。
- Jetpack(AndroidX)を使用します。また、次のバージョン要件を満たしている必要があります。
com.android.tools.build:gradle
v7.3.0 以降compileSdkVersion
28 以降
実機を設定するか、エミュレータを使用してアプリを実行します。
Google Play 開発者サービスに依存している Firebase SDK を使用する場合、デバイスまたはエミュレータに Google Play 開発者サービスがインストールされている必要があります。Google アカウントを使用して Firebase にログインします。
Android プロジェクトがない場合、Firebase プロダクトを試してみるだけであれば、クイックスタート サンプルをダウンロードしてお使いいただけます。
Firebase プロジェクトを作成する
Android アプリに Firebase を追加する前に、Android アプリに接続するための Firebase プロジェクトを作成します。Firebase プロジェクトの詳細については、Firebase プロジェクトについて理解するをご覧ください。
アプリを Firebase に登録する
Android アプリで Firebase を使用するには、アプリを Firebase プロジェクトに登録する必要があります。アプリの登録は、プロジェクトへのアプリの「追加」とも呼ばれます。
Firebaseコンソールに移動します。
プロジェクトの概要ページの中央で、Android アイコン(
)または [アプリを追加] をクリックして、設定ワークフローを起動します。[Android パッケージ名] フィールドにアプリのパッケージ名を入力します。
(省略可)その他のアプリ情報(アプリのニックネームとデバッグ用の署名証明書 SHA-1)を入力します。
[アプリの登録] をクリックします。
Firebase 構成ファイルを追加する
Firebase Android 構成ファイル(
)をダウンロードしてアプリに追加します。google-services.json [Download google-services.json] をクリックして、Firebase Android 構成ファイルを入手します。
構成ファイルをアプリのモジュール(アプリレベル)ルート ディレクトリに移動します。
構成ファイルの値に Firebase SDK からアクセスできるようにするには、Google サービスの Gradle プラグイン(google-services.json google-services
)が必要です。ルートレベル(プロジェクト レベル)の Gradle ファイル(
<project>/build.gradle.kts
または<project>/build.gradle
)で、依存関係として Google サービス プラグインを追加します。Kotlin
plugins { id("com.android.application") version "7.3.0" apply false // ... // Add the dependency for the Google services Gradle plugin id("com.google.gms.google-services") version "4.4.2" apply false }
Groovy
plugins { id 'com.android.application' version '7.3.0' apply false // ... // Add the dependency for the Google services Gradle plugin id 'com.google.gms.google-services' version '4.4.2' apply false }
モジュール(アプリレベル)の Gradle ファイル(通常は
<project>/<app-module>/build.gradle.kts
または<project>/<app-module>/build.gradle
)に、Google サービス プラグインを追加します。Kotlin
plugins { id("com.android.application") // Add the Google services Gradle plugin id("com.google.gms.google-services") // ... }
Groovy
plugins { id 'com.android.application' // Add the Google services Gradle plugin id 'com.google.gms.google-services' // ... }
アプリに Firebase SDK を追加する
モジュール(アプリレベル)の Gradle ファイル(通常は
<project>/<app-module>/build.gradle.kts
または<project>/<app-module>/build.gradle
)に、Android 用 Firebase Cloud Messaging ライブラリの依存関係を追加します。ライブラリのバージョニングの制御には、Firebase Android BoM を使用することをおすすめします。Firebase Cloud Messaging でのエクスペリエンスを最適化するために、Firebase プロジェクトで Google Analytics を有効にして、Google アナリティクス用の Firebase SDK をアプリに追加することをおすすめします。
dependencies { // Import the BoM for the Firebase platform implementation(platform("com.google.firebase:firebase-bom:33.7.0")) // Add the dependencies for the Firebase Cloud Messaging and Analytics libraries // When using the BoM, you don't specify versions in Firebase library dependencies implementation("com.google.firebase:firebase-messaging") implementation("com.google.firebase:firebase-analytics") }
Firebase Android BoM を使用すると、アプリは常に互換性のあるバージョンの Firebase Android ライブラリを使用します。
(代替方法)BoM を使用せずに Firebase ライブラリの依存関係を追加する
Firebase BoM を使用しない場合は、依存関係の行でそれぞれの Firebase ライブラリのバージョンを指定する必要があります。
アプリで複数の Firebase ライブラリを使用する場合は、すべてのバージョンの互換性を確保するため、BoM を使用してライブラリのバージョンを管理することを強くおすすめします。
dependencies { // Add the dependencies for the Firebase Cloud Messaging and Analytics libraries // When NOT using the BoM, you must specify versions in Firebase library dependencies implementation("com.google.firebase:firebase-messaging:24.1.0") implementation("com.google.firebase:firebase-analytics:22.1.2") }
Android プロジェクトを Gradle ファイルと同期します。
クライアント アプリをトピックにサブスクライブする
クライアント アプリは、既存のトピックにサブスクライブすることも、新しいトピックを作成することもできます。クライアント アプリを新しいトピック名(FCM プロジェクトにまだ存在していないトピック名)にサブスクライブすると、その名前の新しいトピックが FCM に作成され、その後すべてのクライアントがそのトピックにサブスクライブできるようになります。
トピックを登録するには、クライアント アプリが Firebase Cloud Messaging subscribeToTopic()
を呼び出し、トピック名 FCM を指定します。このメソッドは Task
を返します。この戻り値により、完了リスナーはサブスクリプションが正常に完了したかどうかを判断できます。
Kotlin
Firebase.messaging.subscribeToTopic("weather") .addOnCompleteListener { task -> var msg = "Subscribed" if (!task.isSuccessful) { msg = "Subscribe failed" } Log.d(TAG, msg) Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() }
Java
FirebaseMessaging.getInstance().subscribeToTopic("weather") .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { String msg = "Subscribed"; if (!task.isSuccessful()) { msg = "Subscribe failed"; } Log.d(TAG, msg); Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); } });
サブスクライブを解除する場合、クライアント アプリは、トピック名を指定して Firebase Cloud Messaging unsubscribeFromTopic()
を呼び出します。
トピック メッセージを受信して処理する
FCM は、他のダウンストリーム メッセージと同じようにトピック メッセージを配信します。
メッセージを受信するには、FirebaseMessagingService
を拡張したサービスを使用します。このサービスは onMessageReceived
コールバックと onDeletedMessages
コールバックをオーバーライドする必要があります。
メッセージを処理するための時間枠は、onMessageReceived
の呼び出しに先立って発生する遅延(OS の遅延、アプリの起動時間、他のオペレーションによってブロックされているメインスレッド、または onMessageReceived
の呼び出し時間がかかりすぎるなど)に応じて 20 秒より短くなる場合があります。この時間が経過すると、Android のプロセス強制終了や Android O のバックグラウンド実行制限などのさまざまな OS 動作により、作業が妨げられる可能性があります。
onMessageReceived
は次の例外を除いて、ほとんどのメッセージ タイプに提供されます。
-
アプリがバックグラウンドで動作しているときに配信された通知メッセージ。この場合、通知はデバイスのシステムトレイに配信されます。ユーザーが通知をタップすると、デフォルトでアプリ ランチャーが開きます。
-
バックグラウンドで受信されたときに通知とデータ ペイロードの両方を持つメッセージ。この場合、通知はデバイスのシステムトレイに配信され、データ ペイロードはランチャー アクティビティのインテントの追加部分に配信されます。
要約:
アプリの状態 | 通知 | データ | 両方 |
---|---|---|---|
フォアグラウンド | onMessageReceived |
onMessageReceived |
onMessageReceived |
バックグラウンド | システムトレイ | onMessageReceived |
通知: システムトレイ データ: インテントの追加部分内 |
アプリ マニフェストの編集
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 では、以下に対してカスタム デフォルト カラーが使用されます。
- 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. }
onDeletedMessages
のオーバーライド
状況によっては、FCM からメッセージが配信されないことがあります。これは、特定のデバイスが接続されたときにそのデバイスにまだ配信されていないアプリのメッセージが多すぎる(100 件を超えている)場合や、デバイスが 1 か月以上 FCM に接続されていない場合に起こります。このような場合、FirebaseMessagingService.onDeletedMessages()
へのコールバックを受け取ることがあります。アプリ インスタンスがこのコールバックを受け取った場合は、アプリサーバーとの完全な同期を実行する必要があります。過去 4 週間以内にそのデバイスのアプリにメッセージを送信していない場合、FCM はonDeletedMessages()
を呼び出しません。バックグラウンド アプリの通知メッセージの処理
アプリがバックグラウンドで動作しているとき、Android ではシステムトレイに通知メッセージが送られます。ユーザーが通知をタップすると、デフォルトでアプリ ランチャーが開きます。
通知とデータ ペイロードの両方を含むメッセージ(および Notifications コンソールから送信されたすべてのメッセージ)がここに含まれます。この場合、通知はデバイスのシステムトレイに配信され、データ ペイロードはランチャー アクティビティのインテントの追加部分に配信されます。
アプリへのメッセージ配信については、FCM レポート ダッシュボードをご覧ください。このダッシュボードには、Android アプリの「インプレッション」(ユーザーが表示した通知)のデータとともに、Apple と Android のデバイスで送信および開封されたメッセージの数が記録されています。
送信リクエストを作成する
トピックを作成した後、クライアント側でクライアント アプリ インスタンスをトピックにサブスクライブするか、またはサーバー API を使用することによって、トピックにメッセージを送信できます。FCM の送信リクエストを初めて作成する場合は、サーバー環境と FCM に関するガイドで、バックグラウンドと設定に関する重要な情報をご確認ください。
バックエンドの送信ロジック内で、次のように目的のトピック名を指定します。
Node.js
// The topic name can be optionally prefixed with "/topics/".
const topic = 'highScores';
const message = {
data: {
score: '850',
time: '2:45'
},
topic: topic
};
// Send a message to devices subscribed to the provided topic.
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
Java
// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";
// See documentation on defining a message payload.
Message message = Message.builder()
.putData("score", "850")
.putData("time", "2:45")
.setTopic(topic)
.build();
// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);
Python
# The topic name can be optionally prefixed with "/topics/".
topic = 'highScores'
# See documentation on defining a message payload.
message = messaging.Message(
data={
'score': '850',
'time': '2:45',
},
topic=topic,
)
# Send a message to the devices subscribed to the provided topic.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)
Go
// The topic name can be optionally prefixed with "/topics/".
topic := "highScores"
// See documentation on defining a message payload.
message := &messaging.Message{
Data: map[string]string{
"score": "850",
"time": "2:45",
},
Topic: topic,
}
// Send a message to the devices subscribed to the provided topic.
response, err := client.Send(ctx, message)
if err != nil {
log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)
C#
// The topic name can be optionally prefixed with "/topics/".
var topic = "highScores";
// See documentation on defining a message payload.
var message = new Message()
{
Data = new Dictionary<string, string>()
{
{ "score", "850" },
{ "time", "2:45" },
},
Topic = topic,
};
// Send a message to the devices subscribed to the provided topic.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);
REST
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":{
"topic" : "foo-bar",
"notification" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message"
}
}
}
cURL コマンド:
curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
"message": {
"topic" : "foo-bar",
"notification": {
"body": "This is a Firebase Cloud Messaging Topic Message!",
"title": "FCM Message"
}
}
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
複数のトピックにメッセージを送信するには、条件を指定します。条件は、ターゲット トピックを指定するブール式です。たとえば次の条件では、TopicA
に加えて TopicB
と TopicC
のどちらか一方にもサブスクライブされているデバイスにメッセージが送信されます。
"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
FCM はまず、かっこ内の条件を評価し、次に左から右に式を評価していきます。上記の式では、いずれか 1 つのトピックのみにサブスクライブしているユーザーにはメッセージは送られません。同様に、TopicA
にサブスクライブしていないユーザーにもメッセージは送られません。メッセージが送られるのは、次の組み合わせにサブスクライブしている場合のみです。
TopicA
とTopicB
TopicA
とTopicC
条件式には最大 5 つのトピックを含めることができます。
条件に基づいてメッセージを送信するには:
Node.js
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
const condition = '\'stock-GOOG\' in topics || \'industry-tech\' in topics';
// See documentation on defining a message payload.
const message = {
notification: {
title: '$FooCorp up 1.43% on the day',
body: '$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
},
condition: condition
};
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
Java
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
String condition = "'stock-GOOG' in topics || 'industry-tech' in topics";
// See documentation on defining a message payload.
Message message = Message.builder()
.setNotification(Notification.builder()
.setTitle("$GOOG up 1.43% on the day")
.setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
.build())
.setCondition(condition)
.build();
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);
Python
# Define a condition which will send to devices which are subscribed
# to either the Google stock or the tech industry topics.
condition = "'stock-GOOG' in topics || 'industry-tech' in topics"
# See documentation on defining a message payload.
message = messaging.Message(
notification=messaging.Notification(
title='$GOOG up 1.43% on the day',
body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
),
condition=condition,
)
# Send a message to devices subscribed to the combination of topics
# specified by the provided condition.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)
Go
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"
// See documentation on defining a message payload.
message := &messaging.Message{
Data: map[string]string{
"score": "850",
"time": "2:45",
},
Condition: condition,
}
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
response, err := client.Send(ctx, message)
if err != nil {
log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)
C#
// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";
// See documentation on defining a message payload.
var message = new Message()
{
Notification = new Notification()
{
Title = "$GOOG up 1.43% on the day",
Body = "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
},
Condition = condition,
};
// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);
REST
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":{
"condition": "'dogs' in topics || 'cats' in topics",
"notification" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message",
}
}
}
cURL コマンド:
curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
"notification": {
"title": "FCM Message",
"body": "This is a Firebase Cloud Messaging Topic Message!",
},
"condition": "'dogs' in topics || 'cats' in topics"
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
次のステップ
- サーバーを使用して、クライアント アプリのインスタンスをトピックにサブスクライブしたり、その他の管理タスクを実行したりできます。サーバーでトピック サブスクリプションを管理するをご覧ください。