[Android]PUSH通知

# 概要
ローカル通知について結構前に勉強したことがあった(リモート通知はどうすればいいのか知らなかった)のだが、
だいぶ忘れた状態で、リモート通知のコード見ててどういうことなの・・・・となって、
なるほどねとなるまでにかなり時間がかかったのでメモ

# PUSH通知とは？
- PUSH通知はその発火トリガーによってローカル通知とリモート通知に分類されている。
    - ローカル通知：発火トリガーがローカルのなにかの処理
    - リモート通知：発火トリガーがFCMからのメッセージ受信
- PUSH通知の本質的な処理・・・すなわち**「通知領域への表示」〜「通知タップ時の画面遷移処理」のやり方は全く同じ** ←ここがわかってなかった
- なお、**単純にPUSH通知という場合はリモート通知のことを指すことが多い。** ←さらにここでやられた
- FCMメッセージ送信=PUSH通知と捉えている記事が世にめちゃくちゃ多いが、コレ多分嘘で、ここが理解を妨げていたんだと思う。  
  Qiita記事とかで**FirebaseからPUSH通知を送信！みたいな図をよく見かけてたせいで、完全にだまされた。** ←こいつが主原因！！！  
  **Firebase公式でPUSH通知なんていう言葉出てこない**もの。送るのはあくまでFCMメッセージだっていってるもの。  

# リモート通知の実装
## デバイストークンの発行〜FCMメッセージが端末に届くまでの大まかなイメージ

[AndroidのPush通知(FCM)をサーバー知識無しで試してみよう](https://qiita.com/flatfisher/items/bdec83caf3c7f9c8917c)のPush通知の「大体の仕組み」を参照。

※本記事では、上記の図で「固有ID」と呼ばれているものを「デバイストークン」、
「サーバー」と呼んでいるものを「アプリサーバー」と呼ぶことにする。
なお、公式ではデバイストークンは「registration token」と呼んでいる模様。

## 1. デバイストークンの取得とアプリサーバーへの登録処理

**実装すべきこと**
- アプリ側: FirebaseMessagingServiceを継承したクラスを定義し、このクラスにonNewToken(token: String)をオーバーライド(★)して、このメソッド内でアプリサーバーへデバイストークンを送信する処理を実装する
- アプリサーバー側: アプリから受け取ったデバイストークンを保存する処理(本記事はアプリ側を扱うので説明は割愛)

**説明**

[FCM SDKをFCMメッセージを送信したい対象のアプリに導入する](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja)と、アプリ初回起動時に、FCM SDKがデバイストークンを発行し、Firebaseにアップロードしてくれる。

なお、これに限らずFirebaseはネット側との通信処理が走るため、FCM SDKのメソッドを叩く際には非同期であるべきらしい。

デバイストークンはアプリ初回起動時以外に以下のケースで新規発行されることもある。

- アプリが新しい端末上に復元された
- アプリがアンインストール/再インストールされた
- アプリ上のデータを削除した

上記のように「FCM SDKがデバイストークンを発行した時」には、FCM SDKによって★のメソッドがコールされるようになっている。
また、★のメソッドの引数tokenにはデバイストークンの文字列が格納されているので、★のメソッド内でアプリサーバーへ送信する処理を実装してやればよい。

なお、デバイストークンはonNewToken(token)メソッド内以外でも、FirebaseMessaging.getInstance().getToken()を実行することでアクセスできる。なお、存在しない場合は新規発行される。

・・・とはいえ、アプリ内でデバイストークンが必要になるタイミングは、デバイストークンが新規発行されてアプリサーバーへ送信したいときだけなので、onNewToken()内の実装だけで事足りそうだが・・・？　

## 2. FCMメッセージが届いた後の処理
アプリがフォアグラウンドにあるときには、FirebaseMessagingServiceを継承したクラスのonMessageReceived()メソッドが呼ばれる。
アプリがバックグラウンド、または起動していない場合は、onMessageReceived()メソッドは呼ばれない。代わりに、通知センターにメッセージが表示され、この通知をタップしたときに起動されるインテントにFCMメッセージが渡される。


**実装すべきこと**  

- アプリ側: FirebaseMessagingServiceを継承したクラスに以下メソッドをオーバーライドして実装
    - onMessageReceived(remoteMessage: RemoteMessage) : FCMメッセージを受け取ったときの処理
    - onMessageDeleted() : 「端末に配信されていないFCMメッセージが100件たまっているとき」「1ヶ月以上FCMとの接続がないとき」にコールされる。これを受けて何らかの処理をしたい場合には実装する。いらないならいらないでOK。

**説明**

onMessageReceived() については、
https://github.com/firebase/quickstart-android/blob/d20b5b4a7e226ff49bef2c20fe0cc01ff23b171a/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/kotlin/MyFirebaseMessagingService.kt#L26-L61
のコメントに実装すべきことが書いてある。

翻訳すると、

```
FCMメッセージに対する処理をonMessageReceived()で実装する。
onMessageReceived内でメッセージが取得できていないならば、その理由は
https://goo.gl/39bRNJ
にあるかもしれない。

メッセージがデータペイロード(※)を含むかチェック
　長時間動くジョブによって処理される必要があるデータであるかチェック
    長時間実行タスク(10秒以上)の場合はワークマネージャーを使う 
    そうでない場合は、10秒以内にメッセージを処理する
　
メッセージが通知ペイロードを含むかチェック

また、受け取ったFCMメッセージの結果として自身の通知の生成を意図する場合は、ここから始めるべきだ。
以下のsendNotificationメソッドを参照しろ。

```

remoteMesage.getData() で、FCMメッセージに含まれていたペイロードデータ(Map<String, Sring>)を取得できる

**※データペイロード**
ヘッダを抜いたデータ本体のこと。httpレスポンスでいうところのbody。



# 参考
- [Firebaseのガイド](https://firebase.google.com/docs/cloud-messaging/android/client?hl=ja)
- [FCM SDK(Android)のリファレンス(FirebaseMesagingService)](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService)
- [AndroidのPush通知(FCM)をサーバー知識無しで試してみよう](https://qiita.com/flatfisher/items/bdec83caf3c7f9c8917c)
- [【FCM】Androidのプッシュ通知「onDeletedMessages」コールバックの処理](https://teratail.com/questions/88774)
- [FirebaseのaddOnSuccessListenerの処理タイミングについての質問
](https://teratail.com/questions/231184)