【Android】FCMでプッシュが来ないとき


#1.Device Tokenが取得できているかチェック
tokenを取得する方法は２パターンあります。

1.FirebaseMessagingServiceを継承したクラスでonNewTokenをオーバーライドする方法

```kotlin:MyFirebaseMessagingService
    override fun onNewToken(token: String) {
        Log.i("FIREBASE", "[SERVICE] Token = $token")
    }
```

2.FirebaseMessaging.getInstance().token.addOnCompleteListenerを実装する方法

```kotlin:MainActivity
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
    if (!task.isSuccessful) {
        Log.w(TAG, "Fetching FCM registration token failed", task.exception)
        return@OnCompleteListener
    }

    // Get new FCM registration token
    val token = task.result

    // Log and toast
    val msg = getString(R.string.msg_token_fmt, token)
    Log.d(TAG, msg)
    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
})
```


1.の方法はDeviceTokenが発行された以下のタイミングのみ呼び出されます。

- アプリが新しいデバイスで復元される場合
- ーザーがアプリをアンインストール / 再インストールする場合
- ユーザーがアプリのデータを消去する場合

2は__このコードを通る度に呼び出されてしまう__ので注意しましょう



ちなみにtokenが発行される前に

```kotlin:MainActivity
FirebaseMessaging.getInstance().getToken()
```

とやっても取得できないので注意しましょう


###DeviceTokenが取れない場合
Firebaseのセットアップがうまくいってない可能性が高いです。
[公式ガイド](https://firebase.google.com/docs/android/setup?hl=ja)を見直しましょう。

#2.最小構成でプッシュが届くか確認

__必ず対象のアプリをバックグラウンド__にしてから[こちらのサイト](https://fcm.snayak.dev/)でテストして見ましょう

_FCM Registration Token (Device Token)_  には 1.で確認したDeviceTokenを入力します。
_Server Key_ は下のFirebaseConsoleの画面で確認できます。
![スクリーンショット 2021-02-19 5.35.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355138/74510e1a-7bc9-7ece-2308-940cb209addc.png)


_Title_と_Body_を適当に入力して[Push Notification]をクリックするとすぐにpushが届くと思います。

届かない場合はFirebaseMessagingServiceが実装できていない可能性が高いです。

AndroidManifestに

```xml:AndroidManifest
<service
    android:name="{FirebaseMessagingServiceを継承したサービスクラス}"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
```
がきちんと設定されているか見直しましょう。


#3.Jsonの内容をチェック

2.でpushが受信できたけど、運用したいプッシュが届かない場合
サーバーから送信しているJsonを確認します。

FCMのプッシュで使える形式は主に２つあります。

###3-1.通知メッセージ

```json 
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    }
  }
}
```

messageの下にtokenとnotificationがある場合です。

この形式の場合はアプリがフォアグラウンドにある時だけonMessageReceivedが呼び出されます。
__バックグラウンドにある場合は勝手に通知が表示されます。__

なのでこの形式でフォアグラウンドの時にプッシュがこない場合はonMessageReceivedで受信したRemoteMessageからデータを取得して
NotificationManagerを使って自前でプッシュを表示する必要があります。

バックでもフォアでもプッシュがこない場合は、サーバーキーかtokenが間違っている、もしくはタイポしていないかなど確認しましょう。

###3-2.データ メッセージ
```json 
{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data":{
      "Nick" : "Mario",
      "body" : "great match!",
      "Room" : "PortugalVSDenmark"
    }
  }
}
```

notificationを含まず、dataがある場合です。
dataとnotificationの両方がある場合はアプリの挙動としては3-1と同じとなります。
また、「from」、「notification」、「message_type」、「google」や「gcm」は予約語となるので使用できません。

この形式の場合はフォアグラウンドでもバックグラウンドでもonMessageReceivedが呼び出されるのでRemoteMessageからデータを取得してNotificationManagerを使って自前でプッシュを表示する必要があります。

こちらもタイポやtokenの間違いに注意しましょう。

#4.アプリの実装をチェック
3.までで問題がなかった場合、サーバー側の実装には問題ないことが分かりました。
それでも自前のプッシュが表示できない場合はアプリの実装を疑いましょう

プッシュを表示するのに最低限必要な実装は以下のようになります。


```kotlin:MyFirebaseMessagingService
    override fun onMessageReceived(message: RemoteMessage) {
        val title = message.data["title"]
        val body = message.data["body"]
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager.createNotificationChannel(
                    NotificationChannel("channelId", "てすと", NotificationManager.IMPORTANCE_DEFAULT)
            )
        }
        val notificationBuilder =  NotificationCompat.Builder(this, "channelId")
        notificationBuilder
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(title)
                .setContentText(body)
        notificationManager.notify(1, notificationBuilder.build())
    }
```



###4-1 NotificationChannelを設定していない
上のコードにありますがnotificationManager.createNotificationChannelをコールしてチャンネルを設定していない場合とNotificationCompat.Builderの第二引数に設定したチャンネルIDを入れていない場合プッシュが表示されません。
exceptionも発生しないので気付きにくいです。

###4-2 setSmallIconをしていない
この場合クラッシュするのですぐ気付きますが、iconを設定していないとダメみたいです。

###4-3 エミュレーターのクイックブート

エミュレーターで動作確認している場合、クイックブートになっているとプッシュが届かないケースがあるようです。

![スクリーンショット 2021-02-19 7.52.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355138/5c5ff744-c0e2-34c2-4065-65a302dc478f.png)

この場合Cold bootにしてエミュレーターを再起動して見ましょう。