今回も前回に引き続き、
今回は、
- Firebase Cloud Messaging
- Firebase Notifications
Firebase Cloud MessagingとFirebase Notificationsの違い
Firebase Cloud MessagingとFirebase Notificationsはお互いに無関係な機能ではなく密接な関わりがあります。
Firebase Cloud MessagingはAndroidのみならずiOS、

Firebase NotificationsはこのFirebase Cloud MessagingをWeb越しから使うための便利なコンソールという位置づけです。Firebase Notificationsを利用するとFirebase Cloud Messagingを制御するために自前でサーバを用意する必要もありませんし、
Firebase Cloud Messagingのメッセージタイプ
Firebase Cloud Messagingには2種類のメッセージタイプが存在します。メッセージタイプによって受信したときの取り扱い方法が違うので、
次の表にメッセージタイプとその違いを示します。
メッセージタイプ | 概要 | ペイロードサイズ |
---|---|---|
Notification Message | いわゆるプッシュ通知としてユーザ端末で表示されることを意図されたメッセージタイプ。title やbody など、 | 2KB |
Data Message | 利用者が任意のキーバリューペアを設定できる。どのような値をセットするかも自由であるし、 | 4KB |
Notification Message
前出の表のとおり、title
、body
、icon
などのプッシュ通知でよく使われるキーがあらかじめ定義されており、
メッセージは次のようなJSONで表現されます。
{
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", // 送信相手
"notification" : {
"body" : "great match!",
"title" : "Portugal vs. Denmark",
"icon" : "myicon"
}
}
このように、notification
というキーの下に定義済みのキーとセットになる値を指定します。この例ではPortugal vs. Denmark
というタイトルでgreat match!
という本文のプッシュ通知がmyicon
というアイコンで表示されます。
後のAndroidのコード例でも紹介しますが、
定義済みのキーの一例は次の表のとおりです。
キー | 概要 | 備考 |
---|---|---|
title | 通知のタイトル | 必須 |
body | 通知の本文 | 任意 |
sound | 通知のサウンド | 任意 |
icon | 通知のアイコン | Androidのみ。任意 |
badge | バッジ | iOSのみ。任意 |
定義済みのキーは他にもありますので、
Data Message
Notification Messageでは指定できるキーがあらかじめ定められていましたが、
{
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", // 送信相手
"data" : {
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
},
}
このように、data
というキーの下に任意のキーバリューペアを好きなだけ設定することができます。その代わり、
どちらを利用すべきか
これは次節のAndroidコード例で詳しく解説しますが、
Android クライアントでプッシュ通知を受け取る
Firebase Cloud Messagingの概要とメッセージタイプがわかったところで、
セットアップ
AndroidでFirebase Cloud Messagingを利用するにはアプリケーションモジュールのbuild.
に次のdependencyを追加するだけです。
dependencies {
compile 'com.google.firebase:firebase-messaging:9.2.0'
}
Registration Tokenの取得
Firebase Cloud Messagingではメッセージを送信する対象のユーザをRegistration Tokenという識別子で区別します。このRegistration Tokenを指定することであるユーザだけにピンポイントでメッセージを送るといったことが可能になります。
Registration Tokenは前述のセットアップが完了するとアプリインストール時に自動的に生成されるので、
String registrationToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "InstanceID token: " + registrationToken);
実際のアプリケーションでは、
Registration Tokenの更新
Registration Tokenは、
Registration Tokenの更新はFirebaseInstanceIdService
の継承クラスでonTokenRefresh()
メソッドをオーバーライドすることで、
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
}
}
先ほどの例と同様、
また、AndroidManifest.
に忘れずに追加するようにしましょう。
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
プッシュ通知のハンドリング
いよいよプッシュ通知を受け取ってNotificationを表示してみたいと思います。
プッシュ通知はFirebaseMessagingService
の継承クラスで、onMessageReceive()
メソッドをオーバーライドすることでハンドリングすることができます。Firebase Cloud MessagingではNotification MessageとData Messageの2つのメッセージタイプがあることを紹介しましたが、
Notification Messageのハンドリング
Notification Messageを受信したことはRemoteMessage#getNotification()
が非null値になることで判別することができます。次のコードをご覧ください。
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Notification Message");
String title = remoteMessage.getNotification().getTitle();
Log.d(TAG, "Notification Message Title: " + title);
String body = remoteMessage.getNotification().getBody();
Log.d(TAG, "Notification Message Body: " + body);
}
}
}
このように、remoteMessage.
をチェックしてからremoteMessage.
でタイトルを、remoteMessage.
で本文を取り出しています。
Data Messageのハンドリング
Data Messageを受信したことはRemoteMessage#getData()
が非null値になることで判別することができます。次のコードをご覧ください。
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import java.util.Map;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());
if (remoteMessage.getData() != null) {
Log.d(TAG, "Data Message");
Map<String, String> data = remoteMessage.getData();
String customTitle = data.get("custom_title");
Log.d(TAG, "Notification Message Title: " + customTitle);
String customBody = data.get("custom_body");
Log.d(TAG, "Notification Message Body: " + customBody);
}
}
}
こちらも同様にremoteMessage.
をチェックしてからremoteMessage.
でMap<String, String>
を取り出して利用しています。Data Messageは任意のキーバリューペアを指定できるので、
なお、AndroidManifest.
に追加するようにしましょう。
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
フォアグラウンド・バッググラウンド時の挙動の違い
実はメッセージ受信時に必ずFirebaseMessagingService#onMessageReceived()
が呼び出されるとは限りません。メッセージタイプと、
アプリの状態 | Notification Messageの場合 | Data Messageの場合 | 両方の場合 |
---|---|---|---|
フォアグラウンド | onMessageReceived | onMessageReceived | onMessageReceived |
バックグラウンド | System Tray | onMessageReceived | NotificationはSystem Tray、 |
Notification Messageの場合
Notification Messageの場合、onMessageReceived()
が呼び出されます。この際、
反対に、System Tray
というところに通知されます。これは何かというと、


Data Messageの場合
Notification Messageの場合、onMessageReceived()
が呼び出されます。ただしNotification Messageのようにバックグラウンド時には勝手にNotificationを用意してくれたりはしないので、NotificationCompat
とPendingIntent
等を使ってユーザに通知してあげる必要があります。
次のコードをご覧ください。
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (remoteMessage.getData() != null) {
Log.d(TAG, "Data Message");
Map<String, String> data = remoteMessage.getData();
String title = data.get("custom_title");
String body = data.get("custom_body");
sendNotification(title, body);
}
}
private void sendNotification(String title, String body) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_ic_notification)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
このようにsendNotification()
というメソッドを追加し、PendingIntent
やNotification
を自分で用意して端末に通知しています。
少々煩雑な分すべてを自分で細かく制御できるので、
Notification MessageとData Messageの両方を指定した場合
メッセージは次のようにnotification
とdata
の両方を一度に指定することも可能です。
{
"to" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
"notification" : {
"body" : "great match!",
"title" : "Portugal vs. Denmark",
"icon" : "myicon"
},
"data" : {
"Nick" : "Mario",
"Room" : "PortugalVSDenmark"
}
}
この場合、onMessageReceived
に通知されますが、System Tray
に通知され、PendingIntent
のgetExtras()
の中に渡されるという少々わかりづらい仕様となっています。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
String value = getIntent().getExtras().getString(key);
Log.d(TAG, "Key: " + key + " Value: " + value);
}
}
特にそうする理由がないのであれば、
トピックのサブスクライブ・アンサブスクライブ
以上でAndroidクライアントからFirebase Cloud Messagingでメッセージを受信する方法はひととおり確認できましたが、
Firebase Cloud Messagingでは、
トピックはユーザが任意で定めることができ、/all
というトピックや、/rooms1234
というようなトピックを自由に購読・
ユーザがトピックをサブスクライブするコードは次のようになります。
FirebaseMessaging.getInstance().subscribeToTopic("news")
この例では /news
というトピックをサブスクライブしています。もしメッセージがこのトピック宛に送信された場合、
メッセージのアンサブスクライブも同様です。
FirebaseMessaging.getInstance().unsubscribeFromTopic("news");
以後、/news
宛のメッセージを受信しません。
Firebase Notifications を使ってメッセージを送信
大変長らくお待たせしました。すべての準備が整ったので、
FirebaseのWebコンソールからプロジェクトを選択し、

「最初のメッセージを送信」

「メッセージ文」

「詳細オプション」

メッセージを確認して

その後、
ユーザセグメントで絞り込んで送信
「ターゲット」

トピック単位で送信
「トピックのサブスクライブ・
トピックをサブスクライブしてからWebコンソールに反映されるまでには最大1日かかると説明されているので、

単一の端末に送信
「Registration Token」

Firebase Notifications の制限事項
Firebase Notificationsのメッセージタイプは自動的にNotification Messageになりますが、
次節で解説するAPIを利用すると、
APIを使ってメッセージを送信する
これまでも触れてきたように、
自前のサーバとの連携方法はAbout Firebase Cloud Messaging Serverに詳しく解説されています。紹介すると長くなるため本連載では扱いませんが、curl
コマンドでAPI越しにメッセージを送信することが可能です。
基本的なアイディアはこのcurl
コマンドと同じなので理解の助けになる他、
サーバーキーの取得
FirebaseのWebコンソールにログインし、

タブを

Notification Messageの送信
それではさっそくサーバーキーを利用して、curl
コマンドでNotification Messageを送信してみましょう。
curl --header "Authorization: key=[YOUR_SERVER_KEY]" \
--header Content-Type:"application/json" \
https://fcm.googleapis.com/fcm/send \
-d "{\"to\": \"/topics/news\",\"priority\":\"high\",\"notification\": {\"title\": \"this is title\", \"body\": \"this is body\", \"icon\": \"ic_stat_ic_notification\"}}"
--header "Authorization: key=[YOUR_
の部分に先ほど取得したサーバーキーを指定します。あとはhttps://
というエンドポイントにapplication/
としてメッセージを送信しているだけです。
JSONの中では"to":"/topics/
という指定で/news
というトピックに対してメッセージを送信しています。"priority":"hight"
で高プライオリティを指定しています。プライオリティに関してはSetting the priority of a messageをご参照ください。
ここで最も重要なのはその次の"notification":{}
の部分です。ここでtitle
とbody
とicon
を指定してメッセージを送信しています。無事クライアントで受信できれば成功です。
Data Messageの送信
同様にData Messageも送信してみましょう。
curl --header "Authorization: key=[YOUR_SERVER_KEY]" \
--header Content-Type:"application/json" \
https://fcm.googleapis.com/fcm/send \
-d "{\"to\": \"/topics/news\",\"priority\":\"high\",\"data\": {\"custom_title\": \"this is custom title\", \"custom_body\": \"this is custom body\", \"icon\": \"ic_stat_ic_notification\"}}"
メッセージをdata
として送信した以外はNotification Messageとまったく同様です。こちらはキーバリューペアに任意のものを設定することができます。こちらもクライアントで受信できれば成功です。
まとめ
いかがだったでしょうか。
今回はFirebaseの新機能のうち、
さて、