Android8以降だとreact-native-firebase/messagingのプッシュ通知がサイレントになる


はじめに

スマホアプリ開発をしているとプッシュ通知は避けて通れない機能です。
特にReactNativeのようにクロスプラットフォーム開発をしている場合は、iOSAndroidのように異なるOSのプッシュ通知の機構をラップしたライブラリを使うことがほとんどだと思います。
プッシュ通知に限らない話なのですが、ReactNativeで開発する場合、私はreact-native-firebaseを使っています。
認証機能やストレージ、DBなどバックエンドの主要な機能がほとんどまかなえてしまうためです。

話をプッシュ通知に戻すと、react-native-firebaseではreact-native-firebase/messagingを使って通知関係の処理を行います。
しかし、AndroidOSのバージョンによっては通知音やバイブレーションが鳴らない所謂 「サイレント通知」 になります。

今回はreact-native-firebase/messagingを使ってプッシュ通知を受信する場合にAndroid8以降で発生するサイレント通知の対処法を紹介します。

前提条件

今回はサイレント通知に関する対処法の紹介のため、プッシュ通知の受信に関する設定や基本的なイベント処理については割愛します。
※これはまた別記事に起こす予定なので、そちらで解説できればと思います。

なぜAndroid 8だとサイレント通知になる?

サイレント通知となる場合、おそらくAndroid8以降だと思います。
これはAndroid 8(Oreo)から追加された 「通知チャンネル」 というものによるためです。

以下引用です。

Android 8.0(API レベル 26)以降、通知はすべてチャネルに割り当てる必要があります。チャネルごとに、そのチャネルのすべての通知に適用される表示と音声の動作を設定することができます。

従って、プッシュ通知で音やポップアップを表示する場合は通知チャンネルを明示的に指定しなければなりません。
react-native-firebase/messagingのチュートリアルに倣って一連の処理をコーディングした段階では、この通知チャンネルの指定が出来ていない状態のため、Android8以降ではサイレント通知となります。

v5系まではソース上で指定ができた

react-native-firebaseの現在の最新はv6系です。
この通知チャンネルの指定ですが、v5系まではnotifications()というモジュールが用意されていたため、JS上で指定することができました。
しかし、v6系では廃止されたらしく、指定には少し工夫(ネイティブコードで記載)が必要です。

対処方法

上記にもある通りネイティブコードの修正が必要になります。
android/app/src/main/java/com/{appName}にあるMainActivity.javaに下記のようにcreateNotificationChannelメソッドを追加しonCreate()で呼び出すようにしましょう。
importを追加する必要もあるので、忘れないようにしましょう。

MainActivity.java
package hogehoge;

import android.os.Bundle;
import android.os.Build; // 追加
import android.app.NotificationChannel; // 追加
import android.app.NotificationManager; // 追加
import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // チャンネル作成処理を実行
        createNotificationChannel();
    }

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  @Override
  protected String getMainComponentName() {
    return "appname";
  }

  /**
   * 通知チャンネルの作成
   */
  private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      // 通知ID
      String id = "notification";
      // チャンネル名
      CharSequence name = "お知らせ";
      // チャンネル説明
      String description = "アプリからのお知らせ情報を通知します。";
      // 重要度
      int importance = NotificationManager.IMPORTANCE_HIGH;
      //チャンネルインスタンスを作成
      NotificationChannel channel = new NotificationChannel(id, name, importance);
      // 説明を設定
      channel.setDescription(description);

      // チャンネルマネージャインスタンスを作成しチャンネルをセット
      NotificationManager notificationManager = getSystemService(NotificationManager.class);
      notificationManager.createNotificationChannel(channel);
    }
  }
}

こうすることでアプリ起動時に自動的にnotificationというIDで通知チャンネルが作成されます。
通知発行側はこのIDを指定して通知を出す形になります。

さらにここで作成した通知チャンネルをデフォルトとするための設定を行います。
プロジェクトのルートにfirebase.jsonを作成し、以下のようにします。

firebase.json
{
  "react-native": {
    "messaging_android_notification_channel_id": "notification"
  }
}

これでAndroid8でも通知音やバイブレーション、ポップアップが表示されるようになります。

まとめ

今回はreact-native-firebase/messagingを使っている場合に、Android8以降で発生するサイレント通知への対処法をご紹介しました。
今回は、ライブラリのバージョンが上がって特定の機能が使えなくなるというパターンに加えて、OS側もバージョンアップで今まで動作していたコードが機能しなくなるという破壊的な変更が重なったため、ネイティブコードレベルで対応が必要となったケースだと思います。

firebaseが絡んでくるような機能は、大概がアプリのコア機能だと思うので早め早めに対応していくのが吉かなと思います。