【Firebase Notifications】アプリがバックグランドでもPUSH通知を表示する方法

onMessageReceived()が呼び出されない問題

FireBaseコンソールのNotificationsからメッセージを送信すると、アプリがバックグランドの場合、FirebaseMessagingServiceを継承したクラスのonMessageReceived()が呼び出されずに、自動的にステータスバーに通知が表示されます。

この仕様はFCMのドキュメントにも記載されています。

アプリがバックグラウンドで動作しているとき、Android ではシステムトレイに通知メッセージが送られます。ユーザーが通知をタップすると、デフォルトでアプリ ランチャーが開きます。
通知とデータ ペイロードの両方を含むメッセージ(および Notifications コンソールから送信されたすべてのメッセージ)がここに含まれます。通知は端末の通知領域(システムトレイ)に配信され、データ ペイロードはランチャー アクティビティのインテントの追加部分で配信されます。

それでも、アプリがフォアグラウンドじゃない場合でもバナー状のPUSH通知を表示したいことがあると思います。

バックグランドでもPUSH通知を表示する方法

実はアプリがバックグランドにいる状態でもFirebaseMessagingServiceを継承したクラスは呼び出されます。
ただ、onMessageReceived()に到達しないだけなのです。

onMessageReceived()より前に呼び出されるのが、handleIntent(Intent intent)です。
引数のintentにはPUSH通知のメッセージ内容が格納されています。
ちなみに、アプリがフォアグラウンドの場合でも、同様にhandleIntent()は呼び出されます。

FireBaseコンソールでメッセージ作成時にカスタムデータを下記の様に設定して送信します。
スクリーンショット 2017-11-16 0.38.28.png

デバッグモードでIntentの中身を見てみると以下のようになっています。

Bundle[{google.c.a.udt=0, google.sent_time=1510755428922, gcm.notification.e=1, google.c.a.c_id=xxxxxxxxxx, google.c.a.ts=xxxxxxxx, gcm.notification.sound=default, gcm.n.e=1, url=https://xxxx/yyyyy/zzzz, body=ボディ, from=xxxxxxxxx, gcm.notification.sound2=default, title=タイトル, google.message_id=0:xxxxxxxxxxxxx, gcm.notification.body=android---, google.c.a.e=1, collapse_key=jp.co.xxxxxxxxxx}]

bodytitleurlにFireBaseコンソールでメッセージ作成時に設定したカスタムデータが入ってきてますね。

あとはこの値を取得して、通常通りPUSH通知を表示します。
全体的な実装はこうなります。

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void handleIntent(Intent intent) {
        if (intent.getExtras() == null) return;

        Map<String, String> data = new HashMap<>();
        data.put("body", intent.getExtras().getString("body"));
        data.put("title", intent.getExtras().getString("title"));
        data.put("url", intent.getExtras().getString("url"));
        sendNotification(data);
    }

    private void sendNotification(Map<String, String> data) {

        String title = data.get("title");
        String message = data.get("body");
        String url = data.get("url");

        Intent intent = new Intent(this, HomeActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
                PendingIntent.FLAG_ONE_SHOT);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setContentTitle(title)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentText(message)
                .setAutoCancel(true)
                .setPriority(1 /* Notification.PRIORITY_HIGH */)
                .setFullScreenIntent(null, true)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, notificationBuilder.build());

    }

おわりに

これが正しい方法かはわかりませんが、一応問題なく動いています。
他に良い方法があれば教えてください!