Androidへのpush通知を試してみる

# 記事の趣旨

自端末のandroidにpush通知を送るまでの勉強．一応前提は以下．

- クライアントはAndroid 10，使用言語はKotlin
- IDEはAndroid Studioの3.4.2を使用
- 通知機能の実装に関する知識および経験は一切なし
- Googleアカウントは持っているが，Firebaseを使ったことはない

## 1. 前提知識：Firebase Cloud Messaging（FCM）

Androidへのメッセージ通知にはこれを用いるとのこと．公式ページは[こちら](https://firebase.google.com/docs/cloud-messaging/?hl=ja)．

### 1.1 アーキテクチャ

[こちら](https://firebase.google.com/docs/cloud-messaging/fcm-architecture?hl=ja)に書いてある（なぜかこのページだけ英語）．流れとしては以下らしい．

1. サーバ上で所定のプロトコルでメッセージを作成し，FCM backendにリクエストを送付する．
2. FCM backendがメッセージを加工しつつ所定のtransport layerに転送する．Androidの場合はAndroid Transport Layer(ATL)が担当．
3. transport layerが対象となる端末にメッセージを送付する．
4. クライアントがオンラインになったとき，メッセージを受信する．

当然，「どうやって宛先の端末を特定するの？」という疑問が出てくるが，上記リンク先のLifecycleを読む限り，トークンを払い出してもらう形で端末を登録しておく模様．

### 1.2 メッセージの種類

[こちら](https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja)によると，メッセージには以下の2種類があるとのこと．違いは今の時点ではあまり理解していないが，今回扱うのは前者という理解で良いはず．

- クライアントのFCM SDKに処理させる「通知メッセージ」
- クライアントアプリに処理させる「データメッセージ」

使用できるデータプロトコルは「HTTP v1」と「XMPP」の2種類．後者について聞いたことが無かったが，XMLベースのプロトコルらしい．この辺りも後日チェックする．

## 2. とりあえず適当な通知メッセージを送ってみる

### 2.1 Firebaseプロジェクトを作成

Androidクライアントを対象とする情報は[こちら](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja)を読んでいくとわかる．ただし，前提として「FirebaseをAndroidプロジェクトに追加していること」とあるので，まずは[こちら](https://firebase.google.com/docs/android/setup?hl=ja#assistant)を参照しつつ設定しておく．

今回はオプション1の手順に則って対応．ほぼ問題なかったが，1点，Gradleをsyncさせたときにエラーが出現．

```
Dependencies using groupId com.android.support and androidx.* can not be combined but （以下略）
```

調べたところ，`android.support`のパッケージは古く，`androidx.*`に移行する必要がある模様．Android Studioの場合，右クリックメニューの`refactor`から`Migrate to AndroidX...`で一括以降できるのでそれで対応した．ついでに静的解析で「もっと新しいのあるよ！」という指示にも一旦従ってみた結果，dependenciesは最終的に以下となった．

```
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.firebase:firebase-analytics:17.4.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
```

最終手順の`アプリを実行して、Firebase の統合に成功したという認証を Firebase に送信します。`の意味がよくわからなかったが，とりあえずエミュレータでアプリを起動してみたところ，「Firebase がアプリに正常に追加されました。」と出たので，とりあえず問題ないと判断する．

なお，`google-services.json`を再取得する場合には，Firebaseコンソールの左上の歯車アイコンから「プロジェクトを設定」を選択した先にダウンロードリンクが存在する．

### 2.2 AndroidプロジェクトにFCM関連の設定を追加する．

前提をクリアしたので，改めて[こちら](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja)のサイトに戻る．

まずはGCM関連の設定をbuild.gradleおよびAndroidManifest.xmlに追加．大体書いているとおりだが，`android:name=".java.MyFirebaseMessagingService"`のところは，`MyFirebaseMessagingService.kt`（ファイル名は任意に変更して良い）を作成するパッケージ名に合わせて修正すること．詰まったら[クイックスタートのソースコード](https://github.com/firebase/quickstart-android)を読めば解決するはず．最悪，クイックスタートの丸コピペでも良いと思う．

### 2.3 テストメッセージを通知してみる．

上記まで作成したら，通常の手順に則ってapkを作成し，端末にインストールする（エミュレータでは何故か上手くいかなかった）．1度起動すると，確かにトークンが払い出されており，長文のトークンがToastで表示された．

Firebaseプロジェクトに戻り，Cloud Messagingからテストメッセージを送信．タイトルと通知テキストをそれっぽく作成し，ターゲットに今回インストールしたアプリを指定して，他はデフォルトのままで送信．図1のとおり，ほぼラグもなく，通知が届くことを確認した．

![notification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494245/e69318e8-d156-43f4-6e33-219122339591.png)
図1. メッセージ発信後の端末表示．

サーバー経由でメッセージを発信する方法とか，メッセージ受信後のクライアント処理とか，確認すべき情報はまだまだ大量にあるが，今回はいったんここまで．