今回も前回に引き続き、
今回は、
- 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の新機能のうち、
さて、
