It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【Android】GCMを使ってみる

GCM(Google Cloud Messaging for Android)は、

開発者がサーバからAndroidデバイス上のAndroidアプリケーションに対して、
データを送信できるようにする仕組みです。

Developers Consoleの準備

Developers Consoleにて、アプリケーションを登録してAPI実行に必要なKey情報を取得します。

Google Developers Console

1.任意のアプリケーションを作成する

アプリケーションを作成すると、APIコンソールのDashboardProject Numberが表示されます。
この数字は、後ほど作成するGCM受信アプリにて、登録ID(registration ID)取得の際に必要になるので覚えておきます。

f:id:inon29:20140407215605j:plain

2.GCMの設定を有効にする

APIs & authを選択し、表示されるリストの中からGoogle Cloud Messaging for Androidを探してONにします。

f:id:inon29:20140407215837p:plain

3. API Keyを発行する

API Keyを作成するには、左メニューのAPIs & auth > credentialボタンを押下し、
Public API access領域にあるCREATE NEW KEYボタンを押下します 。

f:id:inon29:20140407220058p:plain

以下のように作成するキーを選択するダイアログが表示されるます。
今回は、Pushの確認テストとしてサーバーからリクエストを送るため、Server keyを選択します。

f:id:inon29:20140409212815p:plain

Server keyボタンを押下すると、アクセスを許可するIPアドレスを設定するためのダイアログが表示されるので、
アクセスを許可するグローバルIPを入力します。

f:id:inon29:20140407220350p:plain

処理が正常に完了すると下記のようにAPI keyが発行されます。
このKeyを使用して、サーバー側からのGCMのメッセージを送信します。(後述)

f:id:inon29:20140409213514p:plain

Androidアプリの準備

GCMを受信するためのAndroidアプリケーションを実装します。

1.GSM用のライブラリをeclipseにimportする

Android SDK Managerを起動し、Extras配下のGoogle Play servicesを選択し、
importします。

f:id:inon29:20140410201849p:plain

importが完了すると<ANDROID_HOME>/extras/google/google_play_services/libproject/配下に、google-play-services_libというディレクトリがDLされているので、
これをAndroid Project From Exting Code形式でeclipseにimportします。

次に、GCMを受信する為のプロジェクトを新規で作成し、
Projectを選択 > 右クリックでPropertiesを選択 > Androidから、
先ほど、eclipseにimportしたライブラリプロジェクトを追加します。

これで、Android プロジェクトでGCMクラスを使用するための準備は完了です。

2. GCM受信の準備

GCMの通知を受け取るためのReceiverクラスを実装します。

GCMの受信処理中に端末がスリープしないように、ReceiverクラスはWakefulBroadcastReceiverを継承しています。

WakefulBroadcastReceiverは、処理を実行している間は、デバイスがスリープしないことが保証できる特殊なReceiverクラスです。

public  class GcmBroadcastReceiver extends WakefulBroadcastReceiver {

    public GcmBroadcastReceiver() {
        super();
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // GCMを受信 メッセージ受信用のサービスを起動する
        ComponentName comp = 
                new ComponentName(context.getPackageName(), GcmIntentService.class.getName());
              startWakefulService(context, (intent.setComponent(comp)));
              setResultCode(Activity.RESULT_OK);
    }
}

Receiverクラスが受け取ったGCMのインテントを処理するServiceクラスを作成します。

public class GcmIntentService extends IntentService {
    private static final String TAG = "GcmIntentService";
    
    // 引数なしのコンストラクタを作成しないと例外で落ちてしまった
    public GcmIntentService() {
        super(GcmIntentService.class.getName());
    }
    
    public GcmIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Bundle extras = intent.getExtras();

        // intentからGCMのメッセージを取得する
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        String messageType = gcm.getMessageType(intent);
 
        // GCMのメッセージをタイプ別にフィルタリングする。
        //  将来的に拡張されることを考慮し、存在しないタイプを無視するようにする
        if (!extras.isEmpty()) {
            if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { 
                // エラー
                Log.d(TAG,"messageType: " + messageType + ",send error:" + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
                // サーバー側でメッセージを削除された
                Log.d(TAG,"messageType: " + messageType + ",message deleted:" + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) { 
                // メッセージ受信
                Log.d(TAG,"messageType: " + messageType + ",received message:" + extras.toString());
            }
        }
        // 処理の終了をReceiverに通知し、ロックを解放する
        GcmBroadcastReceiver.completeWakefulIntent(intent);
    }
}

3. AndroidManifestを更新する

GCMを受け取るためのPermissionおよび、
上記で実装したメッセージ受信するReceiverクラス、
メッセージを処理するServiceクラスの定義をAndroidManifestに記載します。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.gcmtest"
    android:versionCode="1"
    android:versionName="1.0" >

    ・・・

    <!-- Permissionの宣言 -->
    <permission android:name="com.test.gcmtest.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="com.test.gcmtest.permission.C2D_MESSAGE" /> 
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        ・・・        
        <!-- Receiverクラスの宣言 -->
        <receiver
            android:name=".GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="com.test.gcmtest" />
            </intent-filter>
        </receiver>
        <!-- Serviceクラスの宣言 -->
        <service android:name="com.test.gcmtest.GcmIntentService" />
    </application>

・・・

これで、受信側の準備は完了です。

3. GCM送信の準備

次に、サーバーからGCMを送信するために必要なregistration_idを取得するクラスを実装します。

下記サンプルでは、Activity起動時に非同期でregist処理を行っています。
onPostExecute()で取得できる文字列がregistration_idです。


gcm.register()の引数に渡すIDには、Developer Consolに表示されていたProject Numberを指定します。

public class MainActivity extends Activity {
    private GoogleCloudMessaging gcm;
    private Context context;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = getApplicationContext();
        
        // GCMクラスを作成して非同期でregist処理を行う。
        gcm = GoogleCloudMessaging.getInstance(this);
        registerInBackground();
    }
 
    private void registerInBackground() {
        new AsyncTask<Void, Void, String>() {
            @Override
            protected String doInBackground(Void... params) {
                String msg = "";
                try {
                    if (gcm == null) {
                        gcm = GoogleCloudMessaging.getInstance(context);
                    }
                    String regid = gcm.register("<app_id>");
                    msg = regid;
                    Log.d("tag", "Device registered, registration ID=" + msg);
                } catch (IOException ex) {
                    msg = "Error :" + ex.getMessage();
                }
                return msg;
            }
 
            @Override
            protected void onPostExecute(String msg) {
                // registration IDを取得
                // 従来であれば、ここから送信サーバーへregistration IDを送信するような流れになる
                Log.d("tag", msg); 
            }
        }.execute(null, null, null);
    }
}

GCMのテスト送信

最後に、サーバーからのGCMテスト送信を行います。

下記、curlコマンドでは、
Developers Consoleの準備手順で取得したAPI Keyと上記で取得したregistration IDを使って、
作成したAndroidアプリへ”Hello”という文字列を送信しています。

curl --header "Authorization: key=<API Key>" --header Content-Type:"application/json" https://android.googleapis.com/gcm/send -d "{\"registration_ids\":[\"<registration ID>\"],\"data\":{\"message\":\"Hello\"}}"

参考

http://zatomiya.blogspot.jp/2013/05/androidnotification.html
http://dev.classmethod.jp/smartphone/android/gcm/
http://developer.android.com/google/gcm/gs.html
http://developer.android.com/google/gcm/client.html