皆さんが最新の情報や面白い情報を投稿する中で、
Tipsで恐縮ですが、19日目の投稿をします。
GoogleCloudMessagingを導入するにあたって、gcm.jarを利用する際にハマったことと、その対策を共有したいと思います。
始めに必要なProject IDやjarの入手方法はこちらを参考にしてください。
公式はこちら(英語)です。
#僕がハマった点4つ
- Registration IDの再取得
- GCMBroadcastReceiverクラスは固定値を返す
- 単体テストの方法
- GCMRegistrar.setRegisteredOnServerには期限がある
###1. Registration IDの再取得
- Application update
- Backup and restore
上記の条件のときは、発行されるRegistration IDが変わる可能性があります。
そのため、バージョンを確認して、前回起動時と異なっていた場合、
GCMRegiatrar.register(Context context, String senderId)
を叩くというロジックを入れなければなりません。
- バージョンアップしたら、gcm.jar内のプリファで保存しているRegistration IDは自動で消去されます。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int currentVersionCode = getCurrentVersionCode();
/** バージョンが挙がった場合はregIdが異なる可能性がある。 */
if (isNewVersion(currentVersionCode)) {
/** regIdに変更があるかもしれないのでサーバの登録状態を解除する
* regIdも空になる */
GCMRegistrar.setRegisteredOnServer(this, false);
}
setVersionCode(currentVersionCode);
final String regId = GCMRegistrar.getRegistrationId(this);
/** regIdが空の場合、GCMサーバに登録できていないので、
* GCMRegistrar.registerを叩く。 */
((TextView)findViewById(R.id.gcm_text)).setText(regId);
if (TextUtils.isEmpty(regId)) {
/** 結果をGCMIntentService.javaで受け取る */
GCMRegistrar.register(this, Const.SENDER_ID);
}else {
/** GCM登録が問題ないので、自社サーバ の登録期間を延長する*/
GCMRegistrar.setRegisteredOnServer(this, true);
GCMRegistrar.setRegisterOnServerLifespan(this, GCMRegistrar.DEFAULT_ON_SERVER_LIFESPAN_MS);
}
}
@Override
protected void onDestroy() {
GCMRegistrar.onDestroy(this);
super.onDestroy();
}
/**
* 現在のバージョンコードを取得*/
private int getCurrentVersionCode(){
PackageInfo packageInfo = null;
try {
packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return packageInfo.versionCode;
}
/**
* 現在のバージョンコードをプリファに保存*/
private void setVersionCode(int currentVersionCode){
SharedPreferences pref = getSharedPreferences("gcmexample", MODE_PRIVATE);
pref.edit().putInt("versionCode", currentVersionCode).commit();
}
/**
* 現在のバージョンコードとプリファに保存された値を比較*/
private boolean isNewVersion(int currentVersionCode){
SharedPreferences pref = getSharedPreferences("gcmexample", MODE_PRIVATE);
int oldVersionCode = pref.getInt("versionCode", 0);
if (currentVersionCode > oldVersionCode) {
return true;
}
return false;
}
}
GCMRegistrar.getRegistrationId(Context context)
の値が空のときは、
GCMRegiatrar.register(Context context, String senderId)
を呼び、Registration IDを発行しなければなりません。
##2. GCMBroadcastReceiverクラスは固定値を返す
GCMRegiatrar.register(Context context, String senderId)
が呼ばれると、
com.google.android.c2dm.intent.REGISTRATION
のactionが投げられるので、
GCMBroadcastReceiver
を継承したクラスで受け取ります。
GCMBroadcastReceiver
を継承したクラスでは、GCMBaseIntentService
を起動させます。
その際、packageName直下の階層に.GCMIntentService
をくっ付けたクラスを起動しようとするので、異なる階層にGCMBaseIntentService
を継承したクラスを作成していた場合、
Serviceが起動しません。
GCMBroadcastReceiver.java
protected String getGCMIntentServiceClassName(Context context) {
return getDefaultIntentServiceClassName(context);
}
static final String getDefaultIntentServiceClassName(Context context) {
String className = context.getPackageName() +
DEFAULT_INTENT_SERVICE_CLASS_NAME;// = ".GCMIntentService";
return className;
}
そこで、`GCMBroadcastReceiver`クラスを次のように拡張します。
```java:GCMReceiver.java
public class GCMReceiver extends GCMBroadcastReceiver{
@Override
protected String getGCMIntentServiceClassName(Context context) {
/**
* GCMBaseIntentServiceを継承しているクラス名を渡す*/
return GCMIntentService.class.getName();
}
}
こうすることで、Serviceを無事起動できます。
3. 単体テストの方法
Serviceのどのmethodが呼ばれるかは、GCMBaseIntentService
がハンドリングしてくれます。
Pushを受け取ったときは、GCMBaseIntentService
のonMessge
が呼ばれます。
テストのために、adb で broadcastを投げても、そのままでは
GCMBroadcastReceiver
が拾ってくれません。
テストの時には、次の一文をコメントアウトする必要があります。
android:permission="com.google.android.c2dm.permission.SEND"
<receiver
android:name="com.sakebook.android.gcmexample.GCMReceiver"
<!--android:permission="com.google.android.c2dm.permission.SEND" -->>
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.sakebook.android.gcmexample" />
</intent-filter>
</receiver>
こうすることで、
com.google.android.gsf
以外からのbroadcastも受け取るようになります。
今回の例だと、次のようなintentを投げます。
adb shell am broadcast -a com.google.android.c2dm.intent.RECEIVE -n com.sakebook.android.gcmexample/com.sakebook.android.gcmexample.GCMReceiver --es "hoge" "huga"
4. GCMRegistrar.setRegisteredOnServerには期限がある
GCMRegistrar.setRegisteredOnServer(Context context, boolean flag)
は、自社サーバの登録の状態を設定できるmethodです。
このmethodをフラグとして、使う場合には注意が必要です。
一度trueに設定した場合でも、設定してからDEFAULT_ON_SERVER_LIFESPAN_MS
(=7日間)がたった場合、このmethodはfalseを返すようになります。
永続的に自社サーバの登録状態の管理をしたい場合は、
GCMRegistrar.setRegisterOnServerLifespan(Context context, long lifespan)
で、lifespanを延長させてあげなければなりません。
もしくは、期間限定の利用を設定する際にも使えます。
GCMサーバの状態とは直接の関係はないので、ユーティリティとしての使い方をするのが正しい使い方だと思います。
--
以上です。
流れに沿って説明したので、GCMの使う際の参考になれば幸いです。
サンプルのソースコードはGitHubにあるので、ご自由にどうぞ。と言っても、クライアント側だけなので実際に使うにはサーバ側の設定が必要ですのでお忘れなく。