[Google Cloud Messageing]
前回は、GCMを利用するためのGoogle API consoleからサーバーキーを生成するところを説明しました。GCMはクライアントのアプリ(以後、クライアントアプリとします)だけではなく、メッセージを送信するサーバーサイドのプログラム(以後、GCMサーバと区別するためサーバアプリとします)が必要です。
サーバアプリのプログラムはPHPを利用します。どちらから説明すればいいのかは、にわとりが先か卵が先かのような話になりますが、まずは、クライアントアプリの実装から説明します。GCMのサンプルとして、androids-sdkにgcm-demo-clientがありますので、このソースコードを解説する形で説明していきます。
【ご注意 14.11.26】
この記事の実装方法はDeprecatedされています。アンドロイドアプリからGoogle Cloud Messagingを使う方法(第2回)改訂版をご覧ください。
ヘルパーライブラリのインストール
SDK MangerからExtra/Google Cloud Messaging for Android Libraryをインストールします。
サンプルアプリのインポート
Eclipseのワークスペースにサンプルアプリをインポートします。今回はこのサンプルを元にクライアントアプリを作成します。このサンプルを改修して、一度動作させてみると理解が早いと思います。EclipseのFileメニューから、New -> Project… -> Android Project from existing codeを選択して、android-sdk/extras/google/gcm/samples/gcm-demo-clientをインポートします。
クライアントアプリを作成
特に特別な設定は必要ありませんが、Minumum Required SDKはAPI 8を、Target SDKはAPI 17とします。
GCMライブラリをインポート
GCMライブラリをインポートします。私は、android-sdk/extras/google/gcm/samples/gcm-demo-client/gcm.jarをインポートしました。
マニフェストに追記
マニフェストに以下のものを追加します。
<!-- インターネット接続 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- Googleアカウントの取得 --> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <!-- メッセージを受信したらスリープ解除 --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- GCMからのメッセージ受信 --> <permission android:name="jp.co.notice.android.gcm.permission.C2D_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="jp.co.notice.android.gcm.permission.C2D_MESSAGE" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
applicationタグ内にレシーバとサービスのタグを追加します。
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <!-- メッセージ受信 --> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <!-- レジストレーションIDの受信 --> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="jp.co.notice.android.gcm" /> </intent-filter> </receiver> <!-- メッセージ受信するサービス クラス名のデフォルトはGCMIntentServiceとします。 変更する場合は、カスタムBroadcastReceiverを利用して名前を再定義します。 --> <service android:name=".GCMIntentService" />
ソースコードの構成
サンプルと同様のソースコード構成とします。
CommonUtilities.java ServerUtilities.java GCMIntentService.java MainActivity.java
CommonUtilitiesクラス
CommonUtilitiesクラスには、このサンプルで利用する定数や共通のメソッド(メッセージ表示のみ)があります。重要なのは、SERVER_URLとSENDER_IDです。SERVER_URLはサーバアプリのURLを指定します。こちらはまたサーバアプリの実装時に説明します。SENDER_IDは、プロジェクトを生成したときのProject Numberを指定します。
public class CommonUtilities { // 端末のレジストレーションキーを登録・解除するサーバアプリのURLを指定します。 static final String SERVER_URL = "http://www.notice.co.jp/gcm"; // 前回作成したプロジェクトのProject Numberを指定します。 public static final String SENDER_ID = "445593860212"; // 以下、割愛します。 }
ServerUtilitiesクラス
ServerUtilitiesクラスは、GCMから端末が登録されたり、解除されるタイミングで、サーバアプリにレジストレーションIDを送信するクラスです。登録の場合、先ほどのCommonUtilities.SERVER_URLに、”register.php”を付加したもの、解除の場合は、”unregister.php”を呼び出すようになっています。このサンプルでは、以下のようなURLとなります。
実際のアプリでは、ユーザーIDとともに、レジストレーションIDを渡すということが必要かもしれません。
http://www.notice.co.jp/gcm/register.php®Id=レジストレーションID // 登録 http://www.notice.co.jp/gcm/unregister.php®Id=レジストレーションID // 解除
クライアントアプリが起動すると、GCMに登録されていない端末の場合、GCMへの登録処理が実行されます。それと合わせてサーバアプリに登録通知されます。サーバアプリでは、端末のレジストレーションIDを利用して、指定の端末へメッセージを送信します。以下にregisterメソッドとunregisterメソッドに注釈をつけたものを掲載します。
public static boolean register(final Context context, final String regId) { Log.i(TAG, "registering device (regId = " + regId + ")"); // サーバアプリの登録URLを設定する。 String serverUrl = SERVER_URL + "/register.php"; // 端末のレジストレーションIDをパラメータとして付加する。 Map<String, String> params = new HashMap<String, String>(); params.put("regId", regId); // 空き時間間隔(exponential backoff:繰り返すごとに空き時間を大きくして、送信タイミングを分散する)を持って、リトライします。 long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000); for (int i = 1; i <= MAX_ATTEMPTS; i++) { Log.d(TAG, "Attempt #" + i + " to register"); try { displayMessage(context, context.getString(R.string.server_registering, i, MAX_ATTEMPTS)); // サーバアプリにポストします。 post(serverUrl, params); // サーバアプリに登録したことをGCMに登録します。 GCMRegistrar.setRegisteredOnServer(context, true); // 登録メッセージを表示します。 String message = context.getString(R.string.server_registered); CommonUtilities.displayMessage(context, message); // 成功 return true; } catch (IOException e) { Log.e(TAG, "Failed to register on attempt " + i, e); if (i == MAX_ATTEMPTS) { break; } try { Log.d(TAG, "Sleeping for " + backoff + " ms before retry"); Thread.sleep(backoff); } catch (InterruptedException e1) { // 完了前に、アクティビティが終了 Log.d(TAG, "Thread interrupted: abort remaining retries!"); Thread.currentThread().interrupt(); return false; } // 待ち時間を増加 backoff *= 2; } } // エラーメッセージを表示する。 String message = context.getString(R.string.server_register_error, MAX_ATTEMPTS); CommonUtilities.displayMessage(context, message); return false; } public static void unregister(final Context context, final String regId) { Log.i(TAG, "unregistering device (regId = " + regId + ")"); // 端末のレジストレーションIDをパラメータとして付加する。 String serverUrl = SERVER_URL + "/unregister.php"; // 端末のレジストレーションIDをパラメータとして付加する。 Map<String, String> params = new HashMap<String, String>(); params.put("regId", regId); try { // サーバアプリにポストします。 post(serverUrl, params); // サーバアプリから解除したことをGCMに登録します。 GCMRegistrar.setRegisteredOnServer(context, false); // 解除メッセージを表示します。 String message = context.getString(R.string.server_unregistered); CommonUtilities.displayMessage(context, message); } catch (IOException e) { // この時点では、GCMからは解除できているが、サーバアプリからは解除されていないが、再度、解除を実行する必要はない。 // サーバがメッセージ送信するとき、その端末は未登録エラーとなり、その端末の解除が実行される。 String message = context.getString(R.string.server_unregister_error, e.getMessage()); CommonUtilities.displayMessage(context, message); } }
GCMIntentServiceクラス
GCMIntentServiceクラスは、IntentServiceから派生したGCMBaseIntentServiceを継承しています。GCMからのIntentに対して応答するクラスです。このサービスはバックグランドでGCMから受信して、レジストレーションIDの登録・削除したり、メッセージを受信して、クライアントアプリとステータスバーに通知します。オーバーライドしないといけない主要なメソッドは次のものです。
protected void onRegistered(Context context, String registrationId) protected void onUnregistered(Context context, String registrationId) protected void onMessage(Context context, Intent intent) public void onError(Context context, String errorId)
onRegisteredとonUnregisteredは、それぞれServerUtilitiesクラスのメソッドを使って、登録・解除を実行します。onMessageは今回実装するサーバからのメッセージを処理します。実際はGCMサーバからメッセージが配信されます。onErrorは端末の登録・解除でGCMでエラーが発生したときの処理を実行します。
メインアクティビティ
では、メインアクティビティの実装を見てみましょう。
ブロードキャストレシーバーの定義
GCMIntentServiceからのメッセージを受信するレシーバーです。アクティビティからメッセージを表示するときも、このレシーバーで受信します。
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String newMessage = intent.getExtras().getString(EXTRA_MESSAGE); Log.i(TAG, newMessage); mMessage.append(newMessage + "\n"); } };
初期処理
onCreateでは以下のような処理を実行します。checkDeviceメソッドは、この端末がGCMを実行できる環境かどうかを調べます。checkManifiestメソッドは、このアプリのマニフェストがGCM実行の要件を満たしているか調べます。その後、端末のレジストレーションIDがGCMに登録されているか調べて、未登録なら、登録処理を実行します。そのときサーバアプリに未通知なら、サーバアプリに対して登録処理を呼び出します。
protected void initGCM() { // この端末がGCMを実行できる環境かどうかを調べる。 GCMRegistrar.checkDevice(this); // マニフェストがGCM実行の要件を満たしているか調べる。 GCMRegistrar.checkManifest(this); mMessage = (TextView) findViewById(R.id.message); // メッセージハンドラの登録 registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION)); // GCMに登録されているレジストレーションIDを取得する。 final String regId = GCMRegistrar.getRegistrationId(this); if (regId.equals("")) { // レジストレーションIDがないので、GCMに登録する。 GCMRegistrar.register(this, SENDER_ID); } else { // サーバアプリに登録済みか if (GCMRegistrar.isRegisteredOnServer(this)) { // 登録済みなら、その旨を表示する。 mMessage.append(getString(R.string.already_registered) + "\n"); } else { // 登録されていないなら、サーバアプリへ登録を試みる。 final Context context = this; // GUIスレッドではHTTP通信できないので、非同期で実行する。 mRegisterTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { // サーバアプリへ登録実行 boolean registered = ServerUtilities.register(context, regId); if (!registered) { // 登録できなかったので、解除する。 GCMRegistrar.unregister(context); } return null; } @Override protected void onPostExecute(Void result) { mRegisterTask = null; } }; mRegisterTask.execute(null, null, null); } }
ライフサイクルメソッド
onDestoryで、GCMのリソースを廃棄します。
@Override protected void onDestroy() { if (mRegisterTask != null) { mRegisterTask.cancel(true); } unregisterReceiver(mHandleMessageReceiver); GCMRegistrar.onDestroy(this); super.onDestroy(); }
クライアントアプリの実装が終わったところで、今回は終了です。次回はクライアントアプリを動作させながら、サーバアプリの実装を説明します。
関連記事
- アンドロイドアプリからGoogle Cloud Messagingを使う方法(第1回)- 準備編
- アンドロイドアプリからGoogle Cloud Messagingを使う方法(第2回)- クライアントアプリ編
- アンドロイドアプリからGoogle Cloud Messagingを使う方法(第3回)- サーバーアプリ編