GooglePlayServices8.3以降のPushを導入するStepByStep

いまだにgcm.jarを使っていると、少し前から、警告のような通知を受信するようになりました。
そこで最新版で実装しようとしたのですが、日本語情報があまりなかったので、実装ついでにまとめました。
なお、サーバー側は担当者でないので、curlでのテストまでにとどめます。

# 1. 前提条件

AndroidStudio 1.5+ (Gradle Plugin 1.3+)
GooglePlayServices 8.3+

# 2. Google Developer Consoleでプロジェクトを設定する

**Android Developer Consoleじゃないので要注意。**

## 2.1. 新プロジェクトの作成

(1) [Google Developer Console](https://console.developers.google.com/project?hl=JA&pli=1)へいってサインインする。
(2) 「Google APIを利用する」をクリック
(3) 任意のプロジェクト名を入力し、その他の内容を確認して、「作成」ボタンをクリック

　一番下の利用規約に同意を意味する「はい」を選択しないと作成ボタンが押せるようになりません。
　クリック後、プロジェクトが作成されるまで少し待つ。

## 2.2. GCM APIを有効にする

(1) 「Mobile API」下にある「Cloud Messaging for Android」をクリック
(2) 「APIを有効にする」をクリック
(3) 「認証情報に進む」をクリック
　※表示されない場合はすでにある認証情報を参照すること（プロジェクト画面の左側にあります）

(4) 「認証情報を追加」をクリック
(5) 「APIキー」をクリック
(6) 必要なキーの種類をクリック
　- 「サーバーキー」はサーバーからメッセージを送信する場合に必要。
　- 「Androidキー」「iOSキー」は、端末からもメッセージを送れるようにする場合に必要(?)
　- 複数必要な場合は、(4)~(7)を繰り返す
(7) 任意の名前を付けて保存
(8) 表示されたAPIキーをコピーして保存しておく
　プロジェクト画面の左側からも確認出来ます。

# 3. Androidプロジェクトを設定する

## 3.1. configurationファイルをダウンロードする

(1) [ここ](https://developers.google.com/cloud-messaging/android/client)の**「Get a configuration file」**項目下にある以下のボタンをクリックする

https://developers.google.com/cloud-messaging/android/client
![getconfig.jpg](https://qiita-image-store.s3.amazonaws.com/0/103886/9f5787f6-39f1-b880-fc85-4efc104d9941.jpeg)

(2) AppNameからプロジェクト名を選ぶ
(3) パッケージ名を入力する
　※このパッケージ名は後から変更出来ません(多分)。前もってプロジェクトは作成しておきましょう。
(4) サポートが必要であれば、「Share your Google Mobile Developer Services data...」のチェックボックスにチェックを入れ、日本を選択する
(5) 「Choose and configure services arrow_forward」ボタンをクリック
(6) 「ENABLE GOOGLE CLOUD MESSAGING」ボタンをクリック
　SenderIDをメモしておく。
　※Google Developer Consoleの「ホーム」－「ダッシュボード」の「プロジェクト番号」で後から参照可。
(7) 「Generate configuration files」ボタンをクリック
(8) 「Download google-services.json」をクリックして、ファイルをダウンロードする
(9) (8)でダウンロードしたgoogle-services.jsonファイルを以下に配置する

(プロジェクトフォルダ)/app/google-services.json

※アプリフォルダ名を変更している場合は、「app」を適宜読み替えること

## 3.2. build.gradleに依存関係を記述する

(1) project/build.gradleを編集する

```gradle:project/gradle
dependencies{
  ....
  classpath 'com.google.gms:google-services:1.5.0-beta2'
}
```

(2) app/build.gradleに依存関係を記述する

```gradle:app/gradle
apply plugin: 'com.google.gms.google-services'
....

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

## 3.3. ApplicationManifest.xmlを編集する

・パーミッション関連を追記（重複に注意）
・receiverの記述(**クラス名の変更不可**)
・serviceの記述(3つともクラス名は任意に変更可能)

```xml:ApplicationManifest.xml
    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <permission android:name="<your-package-name>.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="<your-package-name>.permission.C2D_MESSAGE" />

    <application ...>
        <receiver
            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.gcm" />
            </intent-filter>
            <!-- 4.4以前をサポートする場合は以下が必要 -->
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            </intent-filter>
        </receiver>
        <service
            android:name="com.example.MyGcmListenerService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
        <service
            android:name="com.example.MyInstanceIDListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.gms.iid.InstanceID"/>
            </intent-filter>
        </service>
        <service android:name="com.example.gcm.RegistrationIntentService"/>
    </application>
```

**\<your-package-name\>**の箇所は自分のアプリのパッケージ名に変更する。

**com.example.**の部分も任意で変更する。

# 4. javaコードの実装

## 4.1. GcmListenerServiceクラスを実装

```java:MyGcmListenerService.java
public class MyGcmListenerService extends GcmListenerService {
    private static final String TAG = "MyGcmListenerService ";

    @Override
    public void onMessageReceived(String from, Bundle data) {
        // TODO
        String message = data.getString("message");
        Log.d(TAG, "From: " + from);
        Log.d(TAG, "Message: " + message);

        if (from.startsWith("/topics/")) {
            // message received from some topic.
        } else {
            // normal downstream message.
        }
    }
}
```

## 4.2. InstanceIDListenerServiceクラスを実装

```java:MyInstanceIDListenerService.java
public class MyInstanceIDListenerService extends InstanceIDListenerService {

    @Override
    public void onTokenRefresh() {
        // Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
        Intent intent = new Intent(this, RegistrationIntentService.class);
        startService(intent);
    }
}
```

## 4.3. RegistrationIntentServiceクラスを実装

```java:RegistrationIntentService.java
    private static final String TAG = "Registration service";

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public RegistrationIntentService() {
        super("Registration service");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            InstanceID instanceID = InstanceID.getInstance(this);
            String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
                    GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
            Log.d(TAG, ":token=" + token);
            //
            registerGcmId(token);
        } catch (IOException e) {
            Log.d(TAG, ":IO Exception " + e.getMessage());
        }
    }

    protected void registerGcmId(String token){
        // TODO このトークンをPush送信用サーバーに送信する
    }
```

ここで撮れたトークンIDを後述のcurlでの送信テストでも使います。

## 4.4. 起動時の処理を実装

```java:MainActivity.java

    public void onCreate(...){
        // GooglePlayServiceがあれば、PUSHを登録
        if (checkPlayServices()) {
            // Start IntentService to register this application with GCM.
            Intent intent = new Intent(this, RegistrationIntentService.class);
            startService(intent);
        }
    }

    private boolean checkPlayServices() {
        GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
        int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (apiAvailability.isUserResolvableError(resultCode)) {
                apiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
                        .show();
            } else {
                Log.d(TAG, "This device is not supported.");
                finish();
            }
            return false;
        }
        return true;
    }
```

# 5. 送信テスト

 curlを使います。Windowsの人は頑張って入れて下さい。

```
$ curl --header "Authorization: key=サーバーAPIキー" --header Content-Type:"application/json" https://android.googleapis.com/gcm/send -d "{\"registration_ids\":[\"デバイストークンID\"],\"data\":{\"message\":\"Hello\"}}"
```

　**サーバーAPIキー**と**デバイストークンID**を自分の物に置き換えて下さい。
　※連続して何度も送っていると、制限が入るようで、時間が異様にかかることがあります。

# 6. 参考ページなど

https://developers.google.com/cloud-messaging/android/client