GCM 2.0から3.0に移行した話(Androidとサーバー)


今回移行する機会があったので、備忘録もかねて記しておきます。お気づきの点等あればご指摘いただけると幸いです。

2.0ベース実装は前任が担当で、自分にその経験があった訳でもないので、影響範囲の調査からスタートしました。
結論から言うと簡単でした。実装で言えば1人で2時間程度。

3.0では、TopicsやDevice Groupsといった2.0にはない送信方法もありますが、今回これらは使わず、以前からある普通の、registration ID単位での送信を使ってます。

# Android側
ざっくり手順です。

1. build.gradle等の設定
2. 独自のBroadcastReceiver実装を廃止し、代わりにcom.google.android.gms.gcm.GcmReceiverに受信処理を委譲。
3. IntentServiceを拡張していたクラスを、GcmListenerServiceの拡張クラスに置き換え。
4. トークン更新にまつわるサービスを2つ実装（Googleドキュメントで言うMyInstanceIDListenerServiceとRegistrationIntentService)

## 実装

マニフェストはこんな感じになりました。

```AndroidManifest.xml
         <!-- 上の1の部分 -->
         <receiver
-            android:name="com.example.android.service.GcmBroadcastReceiver"
android:name="com.example.android.service.GcmBroadcastReceiver"  +            android:name="com.google.android.gms.gcm.GcmReceiver"
 +            android:exported="true"
              android:permission="com.google.android.c2dm.permission.SEND" >
              <intent-filter>
                  <action android:name="com.google.android.c2dm.intent.RECEIVE" />
 -                <category android:name="com.example.android.permission.C2D_MESSAGE" />
 +                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
 +                <category android:name="com.example.android" />
              </intent-filter>
          </receiver>
          <!-- 同2の部分 -->
 -        <service android:name="com.example.android.service.GcmIntentService" />
 +        <service
 +            android:name="com.example.android.service.MyGcmListenerService"
 +            android:exported="false">
 +            <intent-filter>
 +                <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
 +            </intent-filter>
 +        </service>
 +        <!-- 同3 -->
 +        <service
 +            android:name="com.example.android.service.MyInstanceIDListenerService"
 +            android:exported="false">
 +            <intent-filter>
 +                <action android:name="com.google.android.gms.iid.InstanceID"/>
 +            </intent-filter>
 +        </service>
```

以下、個別クラスの実装について書きます。

### 1. build.gradle周りの設定
基本、参照に記したGoogleドキュメントの通りに設定すればよいです。

プロジェクトルートのbulid.gradleにplay-servicesのclasspassを設定

```
classpath 'com.google.gms:google-services:1.5.0-beta2'
```

app/build.gradleに以下をそれぞれ設定

* plugin。これは、playserivceにまつわるAndroid Studioの便利機能を有効にするためのものっぽいので、それらが不要なら付けなくてよさそうです（確認してない）。

```
apply plugin: 'com.google.gms.google-services'
```
* play-serviceのライブラリの依存関係を記述。

```
dependencies {
  compile "com.google.android.gms:play-services:8.3.0"
}
```

### 2. 独自のBroadcastReceiverの廃止
GCM受信を担っていたBroadcastReceiverは削除します。
`com.google.android.gms.gcm.GcmReceiver`については、アプリ側で必要な実装はありません。

### 3. IntentServiceを拡張していたクラスを、GcmListenerServiceの拡張クラスに置き換える
これまでは、2.で消し去ったBroadcastReceiverからIntentServiceを起動し、`onHandleIntent()`にてノーティフィケーションバーに通知を出すなどの処理をしていたと思いますが、この、onHandleIntent()の処理をまるごと、GcmListenerServiceの`onMessageReceived()`に移せばOKです。
変わるところと言えば、`onHandleIntent()`で渡ってきていたIntentの変わりに、`onMessageReceived()`ではBundleが渡ってくるので、IntentからBundleを抜き出す処理が無くなることぐらいです。

### 4. MyInstanceIDListenerServiceとRegistrationIntentServiceの実装
これはGoogleのサンプルを見るのが早いでしょう。

[MyInstanceIDListenerService]
https://github.com/googlesamples/google-services/blob/master/android/gcm/app/src/main/java/gcm/play/android/samples/com/gcmquickstart/MyInstanceIDListenerService.java
[RegistrationIntentService]
https://github.com/googlesamples/google-services/blob/master/android/gcm/app/src/main/java/gcm/play/android/samples/com/gcmquickstart/RegistrationIntentService.java

アプリ独自でRegistration IDを取得する実装を書く場合には、RegistrationIntentServiceから以下のように抜粋してくればよいでしょう。

```java:Activityとか
import com.google.android.gms.iid.InstanceID;
import com.example.R;
...

	// SENDER_IDには自身のアプリのIDを入れる
	String senderId = context.getString(SENDER_ID);
	InstanceID insId = InstanceID.getInstance(context);
	if (enabled) {
		// GCM有効にする場合はこちら
		regId = insId.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE);
	} else {
		// 無効にする場合はこちら
		insId.deleteToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE);
	}
```

# サーバー側
こちらはとてもらくちんでエンドポイントを3.0用のそれに変更するだけでした。
送信するデータ構造などは2.0から引き継げるようです。

```
-	my $url = 'https://android.googleapis.com/gcm/send'; # GCM 2.0
+	my $url = 'https://gcm-http.googleapis.com/gcm/send'; # GCM 3.0
```

# 参照
Googleのドキュメント。

* Android側：
https://developers.google.com/cloud-messaging/android/client
* サーバー側(HTTP)：https://developers.google.com/cloud-messaging/http