まずは、Google Cloud Messaging for Android(以下GCM)について試してみました。(ちょっと前までは、C2DMと呼ばれていたものです。)
動作は以下の図のように、Androidデバイスからregister idというものを生成及び登録し、その後サーバなどから登録されたデバイスに対してプッシュすることで、メッセージをリアルタイムに通知するという単純なものです。
実装については、基本的にはこちらのページ通りにやっていけばできると思いますが、ポイントとハマったところをまとめていければと思っています。また、サーバサイドのプッシュをPHPでやっている記事は多いですが、チュートリアルにあるサーブレットでやっている人が少なそうなので、今回はJavaサーブレットを使ってみたいと思います。
環境
Apache Tomcat 7
Android 4.1
API KeyとSENDER ID取得
https://code.google.com/apis/consoleからAPI KeyとSENDER IDを取得します。簡単なので詳しくは、http://developer.android.com/guide/google/gcm/gs.htmlを参照ください。
サーブレット作成
まずは、サーバサイドの仕組みを作るため、プロジェクト(Eclipse for JavaEEを使った場合は、Dynamic Web Project)を作成します。
AndroidをEclipseで開発するために、Android Development Tools(ADT)pluginをインストールして、Android SDK Managerで「Extras」⇒「Google Cloud Messaging for Android」を選択しGCMをダウンロードします。そのダウンロードしたディレクトリの中のgcm/gcm-server/ dist/gcm-server.jarをプロジェクトにコピーします。(WEB-INF/libなどへ)
ただ、このjarファイルだけですと、java.lang.ClassNotFoundExceptionが発生します。
simple-1.1.jarがないため発生してるようなので、同様にコピーします。
ただ、このjarファイルだけですと、java.lang.ClassNotFoundExceptionが発生します。
java.lang.ClassNotFoundException: org.json.simple.parser.ParseExceptionこれは、gcm/gcm-server/lib/json_
では、早速サーバサイドのコードですが、3つのサーブレットを作ることとします。1つはregister IDの登録、2つ目はregister IDの登録解除、最後はメッセージ送信。
①. register ID登録サーブレット
①. register ID登録サーブレット
Androidから受け取ったregister IDを適当なデータベースなどに登録しておきます。ここはデータベースではなくてもファイルでもキャッシュでも何でもいいです。
②. register ID登録解除サーブレット
②. register ID登録解除サーブレット
単純に①で登録したものを削除します。
③. メッセージ送信サーブレット(サーバからクライアントへプッシュ)
Message.Builder builder = new Message.Builder().delayWhileIdle(true); builder.addData("message", "Hello"); // 送信するデータ Message message = builder.build(); String myAppKey = <API Key> List<String> devices =<①で登録されたregister IDのList<String>> Sender sender = new Sender(myAppKey); MulticastResult result = sender.sendNoRetry(message, devices);
これでdevicesに対して、メッセージを送信できます。しかし、メッセージ送信時に以下のエラーが発生しました・・・。
javax.net.ssl. SSLHandshakeException:
sun.security.validator. ValidatorException: PKIX path building failed:
sun.security.provider. certpath. SunCertPathBuilderException:
unable to find valid certification path to requested target
Caused by: sun.security.validator. ValidatorException:
PKIX path building failed: sun.security.provider. certpath. SunCertPathBuilderException:
unable to find valid certification path to requested target
Caused by: sun.security.provider. certpath. SunCertPathBuilderException:
unable to find valid certification path to requested target
これはSSL通信を行うために証明書が必要みたいなので、jssecacertsファイルを作成し、$JAVA_HOME/lib/securityにコピーするで解決できました。( keytoolを使うのもいいですが、私は簡単そうだったのでInstallCert.javaを使いました。)
Androidプロジェクト作成
Androidプロジェクトを作成し、上記でダウンロードしてきたgcm/gcm-client/ dist/gcm.jarをAndroidプロジェクトにコピーします。
次にActivityとServiceを作成します。
javax.net.ssl.
sun.security.validator.
sun.security.provider.
unable to find valid certification path to requested target
Caused by: sun.security.validator.
PKIX path building failed: sun.security.provider.
unable to find valid certification path to requested target
Caused by: sun.security.provider.
unable to find valid certification path to requested target
これはSSL通信を行うために証明書が必要みたいなので、jssecacertsファイルを作成し、$JAVA_HOME/lib/securityにコピーするで解決できました。(
ちなみに以下のコマンドでjssecacertsファイルを作成。
> java InstallCert android.googleapis.com
Androidプロジェクト作成
Androidプロジェクトを作成し、上記でダウンロードしてきたgcm/gcm-client/
次にActivityとServiceを作成します。
AndroidManifest.xml
<permission android:name="com.scratch.permission.C2D_ MESSAGE"
android:protectionLevel=" signature" />
<uses-permission android:name="com.scratch.permission.C2D_ MESSAGE" />
<uses-permission android:name="com.google. android.c2dm.permission. RECEIVE" />
<uses-permission android:name="android. permission.INTERNET" />
android:protectionLevel="
<uses-permission android:name="com.scratch.permission.C2D_
<uses-permission android:name="com.google.
<uses-permission android:name="android.
<uses-permission android:name="android. permission.GET_ACCOUNTS" />
<uses-permission android:name="android. permission.WAKE_LOCK" />
<application
<activity
android:name=".GCMActivity"
android:configChanges=" orientation|keyboardHidden"
android:windowSoftInputMode=" adjustPan" >
<intent-filter>
<action android:name="android.intent. action.MAIN" />
<category android:name="android.intent. category.LAUNCHER" />
</intent-filter>
</activity>
<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" />
<action android:name="com.google. android.c2dm.intent. REGISTRATION" />
<category android:name="com.scratch" />
</intent-filter>
</receiver>
<service android:name=". GCMIntentService" />
</application>
<activity
android:name=".GCMActivity"
android:configChanges="
android:windowSoftInputMode="
<intent-filter>
<action android:name="android.intent.
<category android:name="android.intent.
</intent-filter>
</activity>
<receiver
android:name="com.google.
android:permission="com.
<intent-filter>
<action android:name="com.google.
<action android:name="com.google.
<category android:name="com.scratch" />
</intent-filter>
</receiver>
<service android:name=".
</application>
Serviceクラス(GCMIntentService)
package com.scratch; import android.content.Context; import android.content.Intent; import android.util.Log; import com.google.android.gcm.GCMBaseIntentService; public class GCMIntentService extends GCMBaseIntentService { static String SENDER_ID = <SENDER ID>; final private String TAG = getClass().getName(); @Override protected void onError(Context context, String regId) { Log.v(TAG, "error " + regId); } @Override protected void onMessage(Context context, Intent intent) { Log.v(TAG, "Received message"); Log.v(TAG, intent.getStringExtra("message")); //サーバからプッシュされたデータの受け取り } @Override protected void onRegistered(Context context, String regId) { Log.v(TAG, "registered regId=" + regId); // ここでサーブレット①にリクエストしてregister idを登録。 } @Override protected void onUnregistered(Context context, String regId) { Log.v(TAG, "unregistered " + regId); // ここでサーブレット②にリクエストしてregister idを削除。 } public GCMIntentService() { super(SENDER_ID); // superでSENDER IDを指定するようです。 Log.v(TAG, "create GCMIntentService()"); } }
Activityクラス(GCMActivity)
package com.scratch; import android.app.Activity; import android.os.Bundle; import android.util.Log; import com.google.android.gcm.GCMRegistrar; public class GCMActivity extends Activity { final private String TAG = getClass().getName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // check if this device supports GCM. GCMRegistrar.checkDevice(this); // check if it's right manifest. GCMRegistrar.checkManifest(this); final String regId = GCMRegistrar.getRegistrationId(this); Log.v(TAG, "regId=" + regId); if (regId.equals("")) { // GCMIntentService.onRegisteredがコールされる。 GCMRegistrar.register(this, GCMIntentService.SENDER_ID); } else { Log.v(TAG, "Already exists " + regId); } } }
ちなみに、unregisterはをするにはGCMRegister.unregisterメソッドを使い、
これでAndroidでサーバプッシュができるはずです。一度お試しを!(詰まったらコメントください。)
次回は、AppleのApple Push Notification Service(APNS)についても試していこうと思います。
0 件のコメント:
コメントを投稿