push通知をトリガとしてAndroidに処理を実行させる

# 記事の趣旨

[前の記事](https://qiita.com/nk5jp/items/6b2d3a678911457c2493)でサーバからAndroidにpush通知できることを確認したので，最後にそれをトリガとして，Android側で処理を走らせる方法を確認する．

## 1. Android側の処理方法

[こちら](https://firebase.google.com/docs/cloud-messaging/android/receive?hl=ja)にまとめられているとおり，アプリがフォアグラウンドに存在する場合と，バックグラウンドに存在する場合とで，扱いが微妙に変わる．それぞれ以下に示す．

### 1.1 フォアグラウンドにいる場合：FirebaseMessagingService

アプリがフォアグラウンドにいる場合，`com.google.firebase.messaging.FirebaseMessagingService`パッケージの`FirebaseMessagingService`クラスを拡張することでメッセージを受信できる．[2つ前の記事](https://qiita.com/nk5jp/items/7b7c1e70bde457041bd3)で言うところの`MyFirebaseMessagingService.kt`がそれに該当する．以下ではこれをイジっていうことになる．

`FirebaseMessagingService`クラスの仕様は[こちら](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/FirebaseMessagingService?hl=ja)にリファレンスはまとめられている．いくつかのコールバックメソッドが用意されており，拡張クラスではこれらをoverrideしていくことになる．

- `onDeletedMessages()`：端末が長時間サーバにアクセスしないなど，何かしらの理由でメッセージが長期間放置された結果としてメッセージが削除されたとき．
- `onDestroy()`：何も書いてないので不明．おそらくアプリをキルするときに走る．
- `onMessageReceived(RemoteMessage message)`：メッセージを受信したとき．
- `onMessageSent(String msgId)`：GCMのconnection serverへのメッセージ送信が成功したとき．
- `onNewToken(String token)`：新しいトークンが生成されたとき．
- `onSendError(String msgId, Exception exception)`：GCMのconnection serverへのメッセージ送信が失敗したとき．

### 1.2 バックグラウンドにいる場合：Intentの拡張部分

アプリがバックグラウンドにいる場合，`通知はデバイスのシステムトレイに配信され、データ ペイロードはランチャー アクティビティのインテントの追加部分に配信されます。`と書いてある．色々確認したところ`intent.getExtras()`から取り出せば良い模様．

### 1.3 実装例

サーバから送信されるメッセージは[前の記事](https://qiita.com/nk5jp/items/6b2d3a678911457c2493)とほぼ同様．ただしdata部のみ以下のように修正．

```python
    data={
        'date': '20200628',
        'message': 'これはテストメッセージです'
    }
```

クライアント側のうち`MyFirebaseMessagingService.kt`はこんな感じ．

```kotlin
class MyFirebaseMessagingService: FirebaseMessagingService() {

    override fun onNewToken(token: String) {
        Log.d(TAG, "Refreshed token: $token")
    }

    override fun onMessageReceived(p0: RemoteMessage) {
        super.onMessageReceived(p0)
        val date = p0.data["date"]!!.toInt()
        val message = p0.data["message"]!!

        val dao = NotificationsDAO(this)
        dao.createNotification(MyNotification(-1, date, message)) //MySQLにデータを格納している（説明は省略）
    }
}
```

そして`MainActivity.kt`は以下のとおり．

```kotlin
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
        val dao = NotificationsDAO(this)

        if (intent.getExtras() != null) {
            val date = intent.getExtras().getString("date").toInt()
            val message = intent.getExtras().getString("message")
            dao.createNotification(MyNotification(-1, date, message)) //SQLiteにデータを格納している（説明は省略）
        }

        val messageList = dao.getAll() //SQLiteからデータを全件取得してきている（説明は省略）
        val listView: ListView = findViewById(R.id.listView)
        listView.adapter = MessageListAdapter(this, android.R.layout.simple_list_item_1, messageList.take(10))
    }
}
```

この状態でサーバからリクエストを発出したところ，期待どおり通知が飛び，かつ図1のとおり，そのデータがAndroid側のSQLiteに格納されることを確認できた．（`ListView`の表示が整形されているのは`MessageListAdapter`クラスによるもの．今回は説明を省略する）

![Screenshot_20200628-150346.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494245/8cd59864-a3be-09ac-036e-81456f45df54.png)
図1 データ受信後のアプリの表示．

一通りやりたいことは確認できたので，今回の調査は一旦これで終わり．