Android
プッシュ通知
AndroidStudio
GooglePlay
FirebaseCloudMessaging

AndroidアプリにFirebase Cloud Messagingを実装する(2) - アプリでメッセージを受信する

More than 1 year has passed since last update.

Firebase Cloud Messagingを使って、JavaでAndroidアプリにFCMメッセージ(=プッシュ通知)を導入する方法についてまとめています。

前提

前のエントリ でFirebaseプロジェクトとAndroidアプリを接続する方法について書きましたが、このエントリではそれらの作業が全て完了していることを前提としています。
まだFirebaseプロジェクトとAndroidアプリの接続を行なっていない場合は、前のエントリに書かれている内容を元に接続しておいてください。Android Studio2.2から導入されている「Firebase Assistant」というツールを使えば難しくありません。

このエントリの目的

2つのエントリにわたって、以下いずれかの方法でAndroidアプリにメッセージを送信し、Androidアプリがメッセージを受信するまでを書きます。

  • Firebaseコンソールを使って特定の端末にメッセージを送信する
  • HTTPでGoogleが提供するFCMサーバーにリクエストをかけて、特定の端末にメッセージを送信する

2つ目の本エントリでは、メッセージの受信をアプリに実装する方法と、cURLを使ってアプリにメッセージを送る方法を書いています。サーバサイドの実装までは書きませんが、そっち方面に明るい方はぜひやってみてください。

環境

以下の環境を使って実施しています。

  • macOS High Sierra バージョン10.13.1
  • Android Studio 3.0

サンプル

Githubにて公開しています。

https://github.com/outerlet/MessagingApp

FCMメッセージの種類について

FCMメッセージには2つの種類があります。それぞれについて簡単にまとめると以下のようになります。詳しくはこちらをご覧ください。

メッセージタイプ 概要
通知メッセージ アプリの代わりにFCMが処理するメッセージ。予め定義されたキーを使って送信するため、決められた以外の情報を送ることはできない
データメッセージ アプリが直接処理するメッセージ。メッセージのキーなど、送信側が自由にメッセージのフォーマットを決めることができる

通知メッセージとデータメッセージの違いは色々ありますが、メッセージ送信に使うJSONのフォーマットが違うことと、アプリでメッセージを受信した時の挙動が異なることは覚えておきましょう。
FCMメッセージを受信した時のアプリの挙動は以下の通りです。

メッセージタイプ 受信時(アプリがフォアグラウンド) 受信時(アプリがバックグラウンド)
通知メッセージ タスクトレイに入る  onMessageReceived
データメッセージ onMessageReceived  onMessageReceived

メッセージ受信時のロジックはFirebaseMessagingServiceクラスを継承したサービスのonMessageReceivedメソッドに定義しますが、『onMessageReceived』と書かれているケースではそれが呼び出されるとお考えください。詳細は後述します。

ちなみに、通知メッセージにはデータを含めることもできます。本稿では説明しませんので、両者を併用した場合についてはここを参考になさってください。別の機会に書くことができれば…とは思っています。

メッセージの受信をアプリに実装する

1.サービスの定義

メッセージを受信するためには2つのサービスをアプリに追加する必要があります。それぞれ、下記のクラスを継承して作成します。

スーパークラス 役割
FirebaseInstanceIdService 端末上のアプリを一意に特定するための「端末登録トークン」を取得する
FirebaseMessagingService メッセージを受信する

それぞれのサブクラスを作成します。クラス宣言と実装すべきメソッドは以下のようになります。ここではそれぞれ「MyInstanceIdService」「MyMessagingService」と名付けています。

MyInstanceIdService.java
public class MyInstanceIdService extends FirebaseInstanceIdService {
    @Override
    public void onTokenRefresh() {
        // トークンが更新されたときに呼び出される
    }
}
MyMessagingService.java
public class MyMessagingService extends FirebaseMessagingService {
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        // FCMメッセージを受信したときに呼び出される
    }
}

作成したサービスはAndroidManifest.xmlに登録しておきます。インテントフィルタに設定するAction名はここにあるものをそのままコピペして貰えればOKです。

AndroidManifest.xml
<service android:name=".MyInstanceIdService">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>
<service android:name=".MyMessagingService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

2.端末登録トークンを取得する

「端末登録トークン」(以下「トークン」)は、端末にインストールされているアプリを一意に識別するための文字列です。これを利用してアプリにメッセージを送信します。
なおこのトークンは、ユーザーの事情やサーバ側の都合で更新される可能性があります。常に最新のトークンを取得することが推奨されていますので、アプリ起動時には常に最新のトークンにアクセスするよう実装するのが適当でしょう。

トークンを取得するためのコードは以下の通りです。アプリ起動時に呼び出されるActivityのonCreateなどで実行すればよいと思います。

トークンの取得
String token = FirebaseInstanceId.getInstance().getToken();

既にトークンが取得済みであれば、それが返却されます。まだ取得していなければ、取得処理が実行されます。この処理は自動的に起動されるため、そのためのコードを書く必要はありません。
取得処理の結果トークンが得られると、先ほど作成したFirebaseInstanceIdServiceのサブクラスに実装したonTokenRefresh()が呼び出されます。最新のトークンにアクセスするには、ここで上記のコードを再度呼び出せばOKです。

MyInstanceIdService.java
public class MyInstanceIdService extends FirebaseInstanceIdService {
    @Override
    public void onTokenRefresh() {
        String token = FirebaseInstanceId.getInstance().getToken();

        // tokenをサーバに送信するなど、端末外で永続的に保存するための処理をこのあたりに書きます
        // あとでテストに使用したいので、ここではLogcatに出力しています(本来はNG)
        if (!TextUtils.isEmpty(token)) {
            android.util.Log.d("FCM-TEST", "token = " + token);
        }
    }
}

トークンはFCMメッセージの送信先として使用しますので、アプリ側で利用することを想定していない限り(あるんだろうか…)、メッセージを発信するサーバに送って端末には保存しない方がよいでしょう。
ここまでできたら、一度実行してみてください。Logcatに『token = (取得したトークン)』というフォーマットで表示されるはずです。

3.FCMメッセージを受信する

メッセージを受信すると、先ほど作成したFirebaseMessagingServiceのサブクラスに実装したonMessageReceived(RemoteMessage)が呼び出されます。その中に、メッセージをパースしてどのように扱うかのロジックを定義します。
以降のコードは、このonMessageReceived(RemoteMessage)内に定義するためのコードです。

3-1.通知メッセージに対応する

通知メッセージではどの値がどんなキーで送られてくるか決まっているので、特定のメソッドを使って値を取得することができます。例えば通知のタイトルはgetTitle()で、本文はgetBody()で取得します。

MyMessagingService.java
// 通知メッセージの受信
if (remoteMessage.getNotification() != null) {
    RemoteMessage.Notification notification = remoteMessage.getNotification();
    String title = notification.getTitle();
    String body = notification.getBody();

    android.util.Log.d("FCM-TEST", "メッセージタイプ: 通知\nタイトル: " + title + "\n本文: " + body);
}

3-2.データメッセージに対応する

データメッセージではどの値をどんなキーで送信するか、フォーマットを自由に決めることができます。そのため、getData()メソッドでMapを取得し、任意のキーによって目的の値を取得しています。
ここでは、"subject"というキーでメッセージのタイトルが、"text"というキーでメッセージの本文が送信されているものとします。

MyMessagingService.java
// データメッセージの受信
if (remoteMessage.getData().size() > 0) {
    Map<String, String> data = remoteMessage.getData();
    String subject = data.get("subject");
    String text = data.get("text");

    android.util.Log.d("FCM-TEST", "メッセージタイプ: データ\nタイトル: " + subject + "\n本文: " + text);
}

4.FCMメッセージを送信する

ここまでできれば、FCMメッセージを受信する準備は完了です。

4-1.コンソールから通知メッセージを送信する

Firebaseコンソールから通知メッセージを送信してみましょう。アプリを起動して、2.のロジックで取得されるトークンをLogcatで確認しておいてください。
通知メッセージを送るにはNotifications Composerを使います。初めてNotifications Composerを開くと、以下の画面が表示されます。『最初のメッセージを送信』をクリックしてください。
fcm21.png

2回目以降であれば、今まで送信したメッセージが一覧表示されています。画面左上の『新しいメッセージ』をクリックしてください。
fcm22.png

通知メッセージの作成は以下の画面から行えます。

  • メッセージの本文は『メッセージ文』に入力してください。
  • 送信先を指定するには『単一の端末』にチェックを入れて、『FCM登録トークン』に先ほど確認したトークンを入力してください。
  • メッセージのタイトルは任意ですが、入力したい場合は『詳細オプション』の『タイトル』に入力してください。

全て入力したら『メッセージを送信』をクリックします。ここではメッセージのタイトルを「メッセージタイトル」、本文を「メッセージ本文」としておきます。
fcm23.png

メッセージを受信した時の挙動は、アプリがフォアグラウンドにあるかバックグラウンドにあるかで異なります。フォアグラウンドにある時は、onMessageReceived(RemoteMessage)に定義したロジックが呼び出されます。3-1.にあるロジックだと、Logcatに以下のメッセージが表示されます。

D/FCM-TEST: メッセージタイプ: 通知
            タイトル: メッセージタイトル
            本文: メッセージ本文

アプリがバックグラウンドにあると、メッセージはタスクトレイに入ります。タップするとアプリが起動します。
fcm24.png

4-2.cURLを使ってFCMメッセージを送信する

次に、cURLを使って通知メッセージとデータメッセージの両方を送信してみます。なおcURLの導入や使用方法については本稿の主旨から外れますので、ここでは割愛します。

まず、サーバキーを確認します。
Firebaseコンソールで歯車型の設定アイコン①をクリックして画面を開きます。設定画面では『クラウド メッセージング』タブ②をクリックしてください。『サーバーキー』③が表示されますのでコピーしておいてください。
fcm27.png

通知メッセージを送信するコマンドは以下の通りです。メッセージの内容は、先ほどNotifications Composerで入力したのと同じにしてあります。送信方法が異なるだけですので、受信時の挙動も同じです。

api_key=(サーバーキー)
token=(トークン)

curl --header "Authorization: key=$api_key" \
     --header Content-Type:"application/json" \
     https://fcm.googleapis.com/fcm/send \
     -d "{\"to\":\"$token\", \"notification\":{\"title\":\"メッセージタイトル\",\"body\":\"メッセージ本文\"}}"

データメッセージを送信するコマンドは以下の通りです。メッセージの内容は通知メッセージと同じですが、JSONのキーが異なることを確認してください。

api_key=(サーバーキー)
token=(トークン)

curl --header "Authorization: key=$api_key" \
    --header Content-Type:"application/json" \
    https://fcm.googleapis.com/fcm/send \
    -d "{\"to\":\"$token\", \"data\":{\"subject\":\"メッセージタイトル\",\"text\":\"メッセージ本文\"}}"

また、受信時の挙動も異なります。データメッセージの場合はアプリがフォアグラウンドにあってもバックグラウンドにあってもonMessageReceived(RemoteMessage)に定義したロジックが呼び出されるため、Logcatに以下のメッセージが出力される結果となります。メッセージタイプが「通知」でなく「データ」であることにも気をつけてください。

D/FCM-TEST: メッセージタイプ: データ
            タイトル: メッセージタイトル
            本文: メッセージ本文

おわりに

長くなりましたが、今回はコードの作成からメッセージを受信するまでをざっと書いてみました。
ここでは最低限の内容でFCMメッセージを作成していますが、有効期限や優先順位など、メッセージには他にも色々な情報を含めることができます。詳細は以下を参考に、いろんな実装を試してみてください。

  • 通知メッセージとデータメッセージの違いなど、FCMメッセージの詳細についてはこちらで確認してください。
  • 送信時のJSONに定義することのできる内容は、リファレンスで確認できます。

ちなみにFirebaseのようにGoogle Play開発者サービスのSDKに依存するアプリについては、Google Play開発者サービス機能にアクセスする前に、互換性のあるGoogle Play開発者サービスのAPKが端末にインストールされているかどうかをアプリ側でチェックする必要があります。
その方法については、別のエントリで詳しく書きたいと思います。