77
77

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AndroidAdvent Calendar 2013

Day 19

Google Cloud Messagingでハマったこと

Last updated at Posted at 2013-12-19

皆さんが最新の情報や面白い情報を投稿する中で、
Tipsで恐縮ですが、19日目の投稿をします。

GoogleCloudMessagingを導入するにあたって、gcm.jarを利用する際にハマったことと、その対策を共有したいと思います。
始めに必要なProject IDやjarの入手方法はこちらを参考にしてください。
公式はこちら(英語)です。

#僕がハマった点4つ

  1. Registration IDの再取得
  2. GCMBroadcastReceiverクラスは固定値を返す
  3. 単体テストの方法
  4. GCMRegistrar.setRegisteredOnServerには期限がある

###1. Registration IDの再取得

  • Application update
  • Backup and restore

上記の条件のときは、発行されるRegistration IDが変わる可能性があります。
そのため、バージョンを確認して、前回起動時と異なっていた場合、
GCMRegiatrar.register(Context context, String senderId)
を叩くというロジックを入れなければなりません。

  • バージョンアップしたら、gcm.jar内のプリファで保存しているRegistration IDは自動で消去されます。
MainActivity.java
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を受け取ったときは、GCMBaseIntentServiceonMessgeが呼ばれます。
テストのために、adb で broadcastを投げても、そのままでは
GCMBroadcastReceiverが拾ってくれません。
テストの時には、次の一文をコメントアウトする必要があります。

android:permission="com.google.android.c2dm.permission.SEND"

AndroidManifest.xml
        <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にあるので、ご自由にどうぞ。と言っても、クライアント側だけなので実際に使うにはサーバ側の設定が必要ですのでお忘れなく。

77
77
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
77

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?