コードがたくさんあって大変でしたね。ここまでお疲れ様でした。今回の記事でプッシュ機能は完成です。

前回の記事のMainActivityのソース内、登録ボタンを押したときの処理に、こんな記述があったと思います。

// レジストレーションIDを取得
mProgress = null;
Handler mHandler = new Handler();

mProgress = new ProgressDialog(context);
mProgress.setMessage(RegisterTask.message);

mProgress.show();
RegisterTask t = new RegisterTask(context, mHandler, mProgress, regid,
                prefs, tv_id);
t.start();

これは通信部分をHandlerで処理しているのですが、その中身がこんな感じです

[RegisterTask.java]

public class RegisterTask extends Thread {
	// レスポンスコード
	int res_code;

	Context context;
	Handler mHandler;
	ProgressDialog mProgress;
	TextView tv_id;

	String regid;
	SharedPreferences prefs;

	// レジストレーションIDをPOSTするURL
	String post_url = "○○○○○○○○○.php";

	public static String message = "少々お待ちください……";
	String TAG = "RegisterTask";

	public RegisterTask(Context context, Handler mHandler,
			ProgressDialog mProgress, String regid, SharedPreferences prefs,
			TextView tv_id) {
		this.res_code = 0;

		this.context = context;
		this.mHandler = mHandler;
		this.mProgress = mProgress;
		this.tv_id = tv_id;

		this.regid = regid;
		this.prefs = prefs;
	}

	// スレッド内処理
	public void run() {
		// 時間がかかる処理
		Log.d(TAG, "postします");

		PostData();

		// スレッドが終了した場合、終了したことをHandlerに知らせる。
		mHandler.post(new Runnable() {
			public void run() {
				// ダイアログを消す
				mProgress.dismiss();
}
		});
	}

	// POST
	private void PostData() {
		// 結果
		String ret = "";

		// インスタンス取得
		GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);

		try {
			// GCMサーバーへ登録する
			regid = gcm.register(Const.SENDER_ID);

			Log.d(TAG, "regid:" + regid);
		} catch (IOException e) {
			e.printStackTrace();
		}

		// レジストレーションIDを自分のサーバーへ送信する
		URI url = null;
		try {
			url = new URI(post_url);
			Log.d(TAG, "URLはOK:" + post_url);
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}

		// POSTするJSONをつくる
		JSONObject kv1 = new JSONObject();
		try {
			kv1.put("regid", regid);
			kv1.put("param1", "param");
			kv1.put("param2", -1);
			kv1.put("param3", false);
		} catch (JSONException e2) {
			e2.printStackTrace();
		}

		String json = kv1.toString();
		Log.d(TAG, "jsonデータ:" + json);

		// POSTリクエストを実行
		DefaultHttpClient httpClient = new DefaultHttpClient();
		try {
			Log.d(TAG, "POST開始");
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(new StringEntity(json, "UTF-8"));
			httpPost.setHeader("Content-Type", "application/json");
			httpPost.setHeader("Accept-Encoding", "application/json");
			httpPost.setHeader("Accept-Language", "en-US");

			ret = httpClient.execute(httpPost, new ResponseHandler() {
				@Override
				public String handleResponse(HttpResponse response)
						throws IOException {
					Log.d(TAG, "レスポンスコード:"
							+ response.getStatusLine().getStatusCode());
					res_code = response.getStatusLine().getStatusCode();

					// 正常に受信できた場合は200
					switch (response.getStatusLine().getStatusCode()) {
					case HttpStatus.SC_OK:
						Log.d(TAG, "レスポンス取得に成功");

						// レスポンスデータをエンコード済みの文字列として取得する
						return EntityUtils.toString(response.getEntity(),
								"UTF-8");

					case HttpStatus.SC_NOT_FOUND:
						Log.d(TAG, "データが存在しない");
						return null;

					default:
						Log.d(TAG, "通信エラー");
						return null;
					}
				}
			});
		} catch (IOException e) {
			Log.d(TAG, "通信に失敗:" + e.toString());
		} finally {
			// shutdownすると通信できなくなる
			httpClient.getConnectionManager().shutdown();
		}

		// 受信結果をUIに表示
		Log.d(TAG, "結果:" + ret);

		// 成功したら
		if (res_code == HttpStatus.SC_OK) {
			// レジストレーションIDを端末に保存
			Log.d(TAG, "regid:::" + regid);
			storeRegistrationId(regid);

			//MainActivity画面に反映
			tv_id.setText(regid);
		}
	}

	/*
	 * レジストレーションIDの保存
	 */
	private void storeRegistrationId(String regId) {
		// 今のバージョン
		int currentVersion = 0;
		try {
			PackageInfo packageInfo = context.getPackageManager()
					.getPackageInfo(context.getPackageName(), 0);
			currentVersion = packageInfo.versionCode;
		} catch (NameNotFoundException e) {
		}

		// 保存
		Editor editor = prefs.edit();
		editor.putString(Const.PROPERTY_REG_ID, regId);
		editor.putInt(Const.PROPERTY_APP_VERSION, currentVersion);
		editor.commit();
	}
}

途中、POSTするためのJSONを作っています。
実際に動作させてログを見れば解りますが、ここでは、

{
    "regid"   : 123456,		//int型
    "param1"  : "param",	//String型
    "param2"  : -1,			//int型
    "param3"  : false		//boolean型
}

という形式のJSONを作っています。
このあたりは、どういう形式のJSONを受け取るのか、サーバー側の処理を作成する人と話し合ってください。また、エラーコードやレスポンスについても、サーバーの処理によるので、話し合いが必要です。

さて、これで、レジストレーションIDの登録まで完了しました。
あとはサーバーからのプッシュ通知を受け取るレシーバーを作るだけです。

[GcmBroadcastReceiver.java]

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
	String TAG = "GcmBroadcastReceiver";

	@Override
	public void onReceive(Context context, Intent intent) {
		// 送られてきたデータを受け取る
		GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
		String messageType = gcm.getMessageType(intent);

		// 送られてきたデータのメッセージ
		Bundle extras = intent.getExtras();
		String mess = extras.toString();

		if (!extras.isEmpty()) {
			// エラー
			if (messageType
					.equals(GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR)) {
				Log.d(TAG, "MESSAGE_TYPE_SEND_ERROR:" + mess);
			}
			// サーバーでメッセージ削除
			else if (messageType
					.equals(GoogleCloudMessaging.MESSAGE_TYPE_DELETED)) {
				Log.d(TAG, "MESSAGE_TYPE_DELETED:" + mess);
			}
			// 正常に受信
			else if (messageType
					.equals(GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE)) {
				Resources res = context.getResources();

				Notification n = new Notification(); // Notificationの生成
				n.icon = R.drawable.ic_launcher; // アイコンの設定

				// 通知されたときに通知バーに表示される文章
				n.tickerText = mess + "(short)"; // メッセージの設定
				n.flags = Notification.FLAG_AUTO_CANCEL; // 通知を選択した時に自動的に通知が消えるための設定

				// 通常の着信音を選択する
				Uri uri = RingtoneManager
						.getDefaultUri(RingtoneManager.TYPE_ALARM); // アラーム音
				n.sound = uri; // サウンド

				Intent i = new Intent(context, MainActivity.class);
				i.putExtra("MESS", mess);

				PendingIntent pi = PendingIntent.getActivity(context, 0, i,
						PendingIntent.FLAG_UPDATE_CURRENT);
				// 上から通知バーを下してきたときに表示される文章をセット
				n.setLatestEventInfo(context, res.getString(R.string.app_name),
						mess + "(long)", pi);

				long[] vibrate_ptn = { 0, 100, 300, 1000 }; // 独自バイブレーションパターン
				n.vibrate = vibrate_ptn; // 独自バイブレーションパターンを設定

				n.defaults |= Notification.DEFAULT_LIGHTS; // デフォルトLED点滅パターンを設定

				// NotificationManagerのインスタンス取得
				NotificationManager nm = (NotificationManager) context
						.getSystemService(Context.NOTIFICATION_SERVICE);
				nm.notify(1, n); // 設定したNotificationを通知する
			}
		}
	}
}

受け取った通知に問題がなければ、Notificationを使って、端末のアラームやバイブレーションを動作させて知らせます。
このあたりはそれぞれお好みの仕様にしてください。

最後に、定数を宣言するクラスを追加すれば完成です。
[Const.java]

public final class Const {
	//プロダクトID
	static final String SENDER_ID = "○○○○○○○○○○○○";

	// SharedPreferences用
	public static final String EXTRA_MESSAGE = "message";
	public static final String PROPERTY_REG_ID = "registration_id";
	public static final String PROPERTY_APP_VERSION = "appVersion";
}

ここまで上手くいっていれば、プッシュ通知を受け取ると、下記の画像のようになっているのではないでしょうか。

20131119_191348

20131119_191408

20131119_191421

以上でプッシュ通知のアプリ側の実装は完了です!

あとは受け取ったメッセージを煮るなり焼くなり好きにしてください!

あ、「削除」ボタンを押したときの処理を書くのを忘れていました……。

基本的にはRegisterTaskと同じように、サーバーに情報を投げて、受け取ったサーバー側はプッシュ通知を配信しないようにするなどのときに使います。RegisterTaskのほぼコピペでOKです。

それでは!

 
  • プラ山

    ド素人な私がGCM_test_application2を真似て作っていますが、さっぱり動きません。
    Handlerとlogがいたるところでエラーになったんですが、いちかばちかlogはインポートしました。
    Handlerはクラスで宣言するんですよね?
    その中身の書き方とかさっぱりわからずあと一歩で止まってしまいました。

    もしよかったら、プロジェクトごといただけませんでしょうか。

  • taniguchi

    >プラ山さん
    コードは記事内に全て載せているので、あとはインポートの問題だと思います。
    HandlerはRegisterTaskを別クラスで宣言してインポートしています。
    Eclipseをお使いなら、Ctrl+Shift+Oで適切なインポートを行ってくれるので、それを試してみてください。

Set your Twitter account name in your settings to use the TwitterBar Section.