HMSのプッシュ通知の実装説明（Android側）

# HMSのプッシュ通知の概要
プッシュ通知を一言で説明すると、サーバー側（たとえば企業側）がクライアント側（たとえばユーザー側）に通知を送るためのサービスです。
（実際、相手のプッシュトークンさえ分かれば、クライアント側からHMSのプッシュサーバー経由で相手のクライアントへプッシュ通知を送ることもできるのですが、話がややこしくなるので、ここではあくまでサーバー側からクライアント側へ送るプッシュ通知にのみ焦点を当てます。）

## トークン
プッシュ通知を利用するのに、まず理解しなければならないのはトークンという概念です。プッシュ通知には2種類のトークンがあります。

* プッシュトークン（Push Token）
* アクセストークン（Access Token）

### プッシュトークン
クライアントを特定するためのユニークな番号です。クライアント側がサーバー側にプッシュトークン発行を要求し、サーバー側がプッシュトークンをクライアント側に返します。また、サーバー側がクライアント側にプッシュ通知を送るときに、プッシュトークンを使ってクライアントを特定し、プッシュ通知を送ります。プッシュトークンはクライアント側もサーバー側も使います。

### アクセストークン
サーバー側がクライアント側にプッシュ通知を送るために必要な鍵です。この鍵を持って、特定なプッシュトークン（クライアント）向けにプッシュ通知を送ります。アクセストークンはサーバー側のみ使います。

## プッシュ通知の動作の流れ
オフィシャルサイトの説明によると、プッシュ通知の流れは次の図に示すようになります。
![HMSプッシュ通知説明](https://communityfile-drcn.op.hicloud.com/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20200921162411.62735718651660355395396307214434:50510922010539:2800:5350033020C544DF324FF383039039FCB008AA4CFB8D622D0038038440ACBC84.jpg?needInitFileName=true?needInitFileName=true "HMSプッシュ通知説明")

STEP 1：
クライアントがHmsInstanceIdのgetToken()を呼び出し、プッシュサーバーにプッシュトークンを要求します。

STEP 2：
プッシュサーバーがクライアントにプッシュトークンを返します。

STEP 3：
クライアントが自分のプッシュトークンを自分のサービスのサーバー（アプリサーバー）に送ります。

STEP 4：
アプリサーバーがプッシュサーバーにプッシュ通知を送ります。
（ここではさらに2ステップに分けることができますが、[次章（HMSのプッシュ通知の実装説明（サーバー側））](https://qiita.com/Rei_2020/items/a35ce101599e651ba3ac)で詳細を説明します）

STEP 5：
プッシュサーバーがアプリサーバーから受け取ったプッシュ通知をクライアントに転送します。

STEP 6：
クライアントがプッシュ通知を受け取った結果をプッシュサーバーに知らせます。

STEP 7：
プッシュサーバーをその結果をアプリサーバーに転送します。

# 実装について
実装が必要なステップは４つしかありません。Android側に３ステップとサーバー側に１ステップです。

|ステップ|実装場所|関わるトークン|
|:---:|:---:|:---|
|STEP 1|Android|プッシュトークン|
|STEP 2|Android|プッシュトークン|
|STEP 3|Android|プッシュトークン|
|STEP 4|サーバー|アクセストークン、プッシュトークン|
|STEP 5|－|プッシュトークン|
|STEP 6|－|－|
|STEP 7|－|－|

# Android側の実装
### AppGallery Connectでプッシュサービスを開通
1. AppGallery Connectに入って、[My projects]を選びます。
2. リストから対象アプリに切り替えます。
3. [Growing] -> [Push Kit]に入って、Push Kitを有効にします。
![HMSプッシュ通知開通](https://communityfile-drcn.op.hicloud.com/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20200623111906.62173139828262173618525156528569:50510624020319:2800:F83FFC08FE724E93AF4EEAB41D72415F011B82F03C663AF887E45F22682FDCFD.png?needInitFileName=true "HMSプッシュ通知開通")

### HMS SDKの導入
1. agconnect-services.jsonをプロジェクト内に配置します。
2. プロジェクトのbuild.gradleにSDKを追加します。
3. モジュールのbuild.gradleにSDKを追加します。
4. AndroidManifest.xmlにサービスを追加します。

```XML:AndroidManifest.xml
<service
    android:name=".DemoHmsMessageService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.huawei.push.action.MESSAGING_EVENT"/>
    </intent-filter>
</service>
```

詳細はこちらのオフィシャルサイトをご参照ください。
https://developer.huawei.com/consumer/jp/doc/development/HMSCore-Guides/android-integrating-sdk-0000001050040084

### ソースコード
ステップ１～ステップ３のソースコードは次のようになります。

```Kotlin:GetTokenClass.kt
Single.create<String>{ emitter ->
    // ステップ１＋ステップ２
    val token = HmsInstanceId.getInstance(context).getToken(appId, "HCM")

    // 空のトークンが返ってくる可能性があるので、しっかりチェックしましょう
    if (token.isNotEmpty()) {
        emitter.onSuccess(token)
    } else {
        emitter.onError(Exception("Token is empty"))
    }
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ token ->
    // ステップ３：トークンをサーバーに送ります。
    // サーバーにトークンを送らないと、サーバーがこちらにプッシュ通知が送れません。
},{
    // エラー処理
})
```

オフィシャルサイトによると、getTokenを呼び出しても、空のトークンが返ってくる可能性があるので、HmsMessageServiceを継承し、onNewTokenをオーバーライドしなければなりません。

```Kotlin:YourHmsMessageService.kt
class MainService: HmsMessageService() {
    // ステップ２
    override fun onNewToken(token: String?) {
        // ステップ３：トークンをサーバーに送ります。
        // サーバーにトークンを送らないと、サーバーがこちらにプッシュ通知が送れません。
    }
}
```

# サーバー側の実装
こちらのページをご覧ください。
https://qiita.com/Rei_2020/items/a35ce101599e651ba3ac

# まとめ
プッシュ通知の実装が難しそうに見えますが、実際にやってみたら案外簡単です。トークンの概念をしっかり理解すれば、全体像がすぐ把握できると思います。

# 参考
* オフィシャルのデモ：[https://github.com/HMS-Core/hms-push-clientdemo-android](https://github.com/HMS-Core/hms-push-clientdemo-android)