AndroidビームとPush通知で最強のO2Oアプリを作るAndroidで使えるO2O技術まとめ解説(終)(2/3 ページ)

» 2013年02月12日 18時00分 公開
[金岡徹,TIS コーポレート本部 戦略技術センター]

Push型通知の機能をアプリに組み込む

 実装に入る前にシナリオとGCMを利用してアプリを作成する手順を確認しておきます。シナリオは以下のようなものを想定します。

  1. Android端末Xを持っているユーザーAとAndroid端末Yを持っているユーザーBが同じ部屋に入る
  2. Android端末X、Yにあいさつ可能な(同じ部屋にいる)友達リストが表示される
  3. ユーザーAがあいさつを送りたい友達(ユーザーB)を選択し、あいさつを送る
  4. ユーザーBに通知される
あいさつ(GCM)

 1および2は前回の記事で説明済みなので割愛して、あいさつ可能なユーザーのリストが表示された状態から説明を始めます。3、4の部分をGCMを利用して実現します。

 GCMを利用してアプリを作成するには、以下の3つのステップを実行すればOKです。

  1. API Key、Sender IDの取得
  2. Androidアプリの作成
  3. サードパーティアプリケーションサーバの構築

 では、ここからアプリを実装していきます。

【1】API Key、Sender IDの取得

 GCMを利用するためには「API Key」「Sender ID」を事前に取得しておく必要があります。これらの取得方法は「ソフトウェア技術ドキュメントを勝手に翻訳」の記事が詳しいので参考にしてみてください。本稿ではAPI KeyおよびSenderIDを取得しているものとして話を進めます。

【2】Androidアプリの作成

 Androidアプリのひな型は作成済みであるという前提で説明を進めます。Eclipseから新規プロジェクトを作成しておいてください。

 GCMを利用する際には、Androidアプリ用のライブラリ「gcm.jar」を利用すると便利です。今回はライブラリを利用して実装していきます。ライブラリはAndroid SDK Managerを利用してダウンロードできます。

 ダウンロードしたライブラリは、「YOUR_SDK_ROOT/extras/google/gcm/gcm-client/dist」に格納されていると思いますので、Androidアプリ内の「libs」フォルダ(なければフォルダを作成)以下に配置します。配置しただけでは読み込まれないので、jarファイルを読み込むようにBuild Pathは通しておいてください。

 GCMを利用するには、Permissionの設定を行う必要があります。必要な部分のみを抜粋したので以下を参考に「AndroidManifest.xml」に追記してください。

  1. <uses-sdk android:minSdkVersion="14" />
  2. <uses-permission android:name="android.permission.INTERNET"/>
  3. <permission android:name="com.example.pecorin.permission.C2D_MESSAGE" android:protectionLevel="signature" />
  4. <uses-permission android:name="com.example.pecorin.permission.C2D_MESSAGE" />
  5. <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
  6. <uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
  7. <application
  8. <receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
  9. <intent-filter>
  10. <action android:name="com.google.android.c2dm.intent.RECEIVE" />
  11. <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
  12. <category android:name="com.example.pecorin" />
  13. </intent-filter>
  14. </receiver>
  15. <service android:name=".GCMIntentService"></service>
  16. </application>
AndroidManifest.xml

 最初に、Activity(本稿では「DeviceRegistrationActivity」)に「Push型通知を送りたい端末からGCMサーバに対して端末の登録依頼を行う」処理を記述します。ライブラリが読み込まれていれば、GCMサーバに端末を登録するメソッド「GCMRegistrar.register(Context context, String... senderIds)」が利用可能です。基本的にこのメソッドを実行すればOKです。

 DeviceRegistrationActivityでは、ボタンをタップするとメソッドを実行するように実装しています。

 登録が完了するとGCMからインテントが送信されるので、IntentをハンドリングするServiceを作成し、適切にハンドリングします(ハンドリングはGCMBaseIntentServiceを継承したクラスで行います。この後、説明します)。

 一度登録が成功すると、「GCMRegistrar.getRegistrationId(Context context)」メソッドで端末を一意に特定できるIDを取得できるので、IDを再取得したい場合は、このメソッドを実行します(端末の登録が完了していない場合は空文字を返します)。

  1. public class DeviceRegistrationActivity extends Activity {
  2. private static final String SENDER_ID = "**********";
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.device_registration);
  7. Button button = (Button) findViewById(R.id.registration_button);
  8. button.setText(getString(R.string.registration_button));
  9. button.setOnClickListener(new OnClickListener() {
  10. @Override
  11. public void onClick(View v) {
  12. Context context = getApplicationContext();
  13. try {
  14. // デバイスがGCMに対応しているかどうかをチェック
  15. GCMRegistrar.checkDevice(context);
  16. // GCMを利用するうえでマニフェストが適切に書かれているかどうかをチェック
  17. GCMRegistrar.checkManifest(context);
  18. } catch (UnsupportedOperationException e) {
  19. e.printStackTrace();
  20. } catch (IllegalStateException e) {
  21. e.printStackTrace();
  22. }
  23. // RegistrationIDを取得
  24. final String regId = GCMRegistrar.getRegistrationId(context);
  25. // GCMサーバ(Googleが管理するサーバ)にサインアップしていない場合
  26. if (regId.equals("")) {
  27. // 取得したSender IDを使ってGCMサーバにサインアップする
  28. GCMRegistrar.register(context, SENDER_ID);
  29. } else {
  30. // GCMサーバ(Googleが管理するサーバ)にサインアップしている場合、メッセージを出力する
  31. Toast.makeText(context, "Already registered", Toast.LENGTH_SHORT).show();
  32. }
  33. }
  34. });
  35. }
  36. }
DeviceRegistrationActivity.java

 登録が完了すると、GCMサーバから端末を一意に特定できるIDを含んだIntentが発行されます。このIDをサードパーティアプリケーションサーバに保存するように依頼します。

 GCMサーバから送信されるインテントをハンドリングするためにGCMBaseIntentServiceを継承したクラス(GCMIntentService)を新規に作成します。

 GCMサーバから送信されたインテントを端末側で受け取るタイミングとしては、以下の2つがあります。

  • GCMサーバからメッセージが送信されたとき
  • GCMサーバへの端末登録が完了したとき

 「GCMサーバに端末登録が完了したとき」にはUpLoadRegistrationIdTaskを実行して、サードパーティアプリケーションサーバにIDをアップロードします。UpLoadRegistrationIdTaskでは、uploadRegistrationId(String facebook_id, String regId)というメソッドを実行し、「http://SERVER_URL/device_registrations」にHTTP POSTで非同期にアクセスしています。

 サーバ側の実装については後述しますが、「http://SERVER_URL/device_registrations」にPOSTでアクセスするとfacebook_idとregIdを紐付けてサーバ側のDBに保存させています(regIdだけを保存してもいいのですが、事前に登録してあるユーザー情報と紐づけると管理がしやすくなるので、このような実装にしています)。

 「GCMからメッセージ送信されたとき」にはメッセージを受信したことをユーザーに知らせるためにステータスバーに通知を表示させます。ステータスバーに通知を表示させるにはNotificationManagerを利用します。ここでは、通知をタップしたときにFriendListActivity(アプリを最初に起動したときに表示されるActivity)に遷移するように実装しています。

  1. public class GCMIntentService extends GCMBaseIntentService {
  2. private static final String SENDER_ID = "**********";
  3. private static final String PREFERENCE_KEY = "preference";
  4. private static final String SERVER_URL = "http://";
  5. public GCMIntentService() {
  6. super(SENDER_ID);
  7. }
  8. // GCMからメッセージが送信された場合、このメソッドが呼ばれる
  9. @Override
  10. protected void onMessage(Context context, Intent intent) {
  11. String message = intent.getStringExtra("message");
  12. NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
  13. Notification notification = new Notification(R.drawable.ic_launcher, message, System.currentTimeMillis());
  14. notification.flags = Notification.FLAG_AUTO_CANCEL;
  15. Intent remoteIntent = new Intent(context.getApplicationContext(), FriendList.class);
  16. PendingIntent contentIntent = PendingIntent.getActivity(context.getApplicationContext(), 0, remoteIntent, 0);
  17. notification.setLatestEventInfo(context.getApplicationContext(), context.getString(R.string.app_name), message, contentIntent);
  18. notificationManager.notify(R.string.app_name, notification);
  19. }
  20. // GCMサーバに端末登録が完了した場合、このメソッドが呼ばれる
  21. @Override
  22. protected void onRegistered(Context context, String regId) {
  23. SharedPreferences sharedpref = context.getSharedPreferences(PREFERENCE_KEY, MODE_PRIVATE);
  24. String facebook_id = sharedpref.getString("facebook_id", null);
  25. UpLoadRegistrationIdTask task = new UpLoadRegistrationIdTask(facebook_id, regId, context);
  26. task.execute();
  27. }
  28. // 非同期で行うためにAsyncTaskを利用して、RegistrationIDをサードパーティアプリサーバにアップロード
  29. public class UpLoadRegistrationIdTask extends AsyncTask<Void, Void, Integer> {
  30. private Context context;
  31. private String facebook_id;
  32. private String regId;
  33. public UpLoadRegistrationIdTask(String facebookId, String regId, Context context) {
  34. this.facebook_id = facebookId;
  35. this.regId = regId;
  36. this.context = context;
  37. }
  38. @Override
  39. protected Integer doInBackground(Void... arg0) {
  40. int statusCode = 0;
  41. HttpResponse objResponse = uploadRegistrationId(facebook_id, regId);
  42. statusCode = objResponse.getStatusLine().getStatusCode();
  43. return statusCode;
  44. }
  45. @Override
  46. protected void onPostExecute(Integer statusCode) {
  47. if (statusCode == 201) {
  48. Intent newIntent = new Intent(context, FriendList.class);
  49. newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
  50. context.startActivity(newIntent);
  51. }
  52. }
  53. }
  54. private HttpResponse uploadRegistrationId(String facebook_id, String regId) {
  55. ~(省略)~
  56. }
  57. }
GCMIntentService.java

 友達リストを表示するActivity(FriendListActivity)に選択した友達に対してあいさつを送信できる機能を実装します。リストから友達を選択すると、ダイアログを開き、ダイアログ内に表示されているボタンをタップします(下記の実装ではボタンが1つですが、NFCを活用した機能を実装する際にボタンが増えるため、このような実装にしています)。

 ボタンをタップすると、greetTaskの中で「sender.greet(String recieverFacebookId, String type, String ServerURL)」メソッドを実行し、「http://SERVER_URL/pecori」にHTTP POSTで非同期にアクセスしています。

 サーバ側の実装については後述しますが、「http://SERVER_URL/pecori」にPOSTでアクセスすると、リストから選択した友達の端末のIDとメッセージを併せてGCMサーバに送信します。

  1. public class FriendListActivity extends Activity {
  2. private User mSender;
  3. private ListView mListView;
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. ~(省略)~
  8. LayoutInflater factory = LayoutInflater.from(this);
  9. final View inputView = factory.inflate(R.layout.custom_dialog, null);
  10. AlertDialog.Builder builder = new AlertDialog.Builder(this);
  11. builder.setIcon(R.drawable.ic_launcher).setTitle(R.string.greet_message).setCancelable(true).setView(inputView);
  12. final AlertDialog dialog = builder.create();
  13. mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  14. @Override
  15. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  16. ListView listView = (ListView) parent;
  17. User item = (User) listView.getItemAtPosition(position);
  18. final String recieverFacebookId = item.getFacebookId();
  19. final String recieverName = item.getName();
  20. greetButton.setOnClickListener(new OnClickListener() {
  21. @Override
  22. public void onClick(View v) {
  23. dialog.dismiss();
  24. String type = "gcm";
  25. greetTask task = new greetTask(mSender, type);
  26. task.execute(recieverFacebookId);
  27. }
  28. });
  29. ~(省略)~
  30. }
  31. public class greetTask extends AsyncTask<String, Void, String> {
  32. private User sender;
  33. private String type;
  34. private String ServerURL = "http://";
  35. public greetTask(User sender, String type) {
  36. this.sender = sender;
  37. }
  38. @Override
  39. protected String doInBackground(String... ids) {
  40. String recieverFacebookId = ids[0];
  41. String message = sender.greet(recieverFacebookId, type, ServerURL);
  42. return message;
  43. }
  44. @Override
  45. protected void onPostExecute(String message) {
  46. Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
  47. }
  48. }
  49. }
FriendListActivity.java
  1. public class User {
  2. public String greet(String recieverFacebookId, String type, String ServerUrl) {
  3. ~(省略)~
  4. }
  5. }
User.java

 これでアプリ側の実装は完成です。

【3】サードパーティアプリケーションサーバの構築

 ここからはサードパーティアプリケーションサーバについて説明します。GCMを使ってPush型通知を行うには、「サードパーティアプリケーションサーバ」と呼ばれるサーバ側のアプリケーションが必要です。サードパーティアプリケーションサーバはプラットフォームや言語による縛りはないので、今回はAmazon Web Services上にRuby on Railsを利用してアプリケーションサーバを構築します。

 まず、Android端末から送信された端末のIDをDBに格納します。先の説明でもあったようにユーザー情報と紐づけて管理するため、Userモデルにregistartion_idというカラムを持たせて保存しています。

  1. class DeviceRegistrationsController < ApplicationController
  2. protect_from_forgery :except => :create
  3. def create
  4. facebook_id = params["facebook_id"]
  5. registration_id = params["registration_id"]
  6. if user = User.find_by_facebook_id(facebook_id)
  7. user.registration_id = registration_id
  8. if user.save
  9. render :text => "Completion of registration!", :status => 201
  10. else
  11. render :text => "Failure to register", :status => 500
  12. end
  13. else
  14. render :text => "Failure to register", :status => 500
  15. end
  16. end
  17. end
device_registrations_controller.rb

 Andorid端末からあいさつを送った場合、あいさつを送りたい友達の端末IDが送られてくるので「誰からのあいさつ」かというメッセージを作成して、GCMサーバに指定した端末ID宛てにメッセージを送信するように依頼します。

  1. class PecoriController < ApplicationController
  2. protect_from_forgery :except => :create
  3. def create
  4. sender_facebook_id = params["sender_facebook_id"]
  5. receiver_facebook_id = params["receiver_facebook_id"]
  6. type = params["type"]
  7. receiver_user = User.find_by_facebook_id(receiver_facebook_id)
  8. sender_user = User.find_by_facebook_id(sender_facebook_id)
  9. if receiver_user and sender_user
  10. receiver_registration_id = receiver_user.registration_id
  11. sender_name = sender_user.name
  12. pecori_message = "Pecori from " + sender_name
  13. message = {:message => pecori_message }
  14. registration_ids = []
  15. registration_ids << receiver_registration_id
  16. gcm_result, gcm_result_message = GcmAccess.gcm_post_message(registration_ids, message)
  17. if gcm_result
  18. render :text => gcm_result_message, :status => 200
  19. else
  20. render :text => gcm_result_message, :status => 500
  21. end
  22. end
  23. end
  24. end
pecori_controller.rb

 実際にはGCMサーバにメッセージを送信するように依頼する部分は、「GcmAccess.gcm_post_message(registration_ids, message)」メソッドで行っています。このメソッドでは、引数として渡ってきた端末のID(registration_ids)とメッセージをJSON形式で組み立て直し、指定されたURLに対してHTTPでPOSTしています。

  1. module GcmAccess
  2. require 'net/http'
  3. require 'uri'
  4. require 'json'
  5. API_KEY = "**********"
  6. def self.gcm_post_message(registration_ids, data={ })
  7. url = URI.parse("https://android.googleapis.com/gcm/send")
  8. message_data = {}
  9. message_data["registration_ids"] = registration_ids
  10. message_data["data"] = data
  11. headers = { "Content-Type" => "application/json", "Authorization" => "key=" + API_KEY }
  12. http = Net::HTTP.new(url.host, url.port)
  13. http.use_ssl = true
  14. # 動作確認のみ行いたいので、一時的にOpenSSL::SSL::VERIFY_NONE
  15. http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  16. res = http.post(url.path, message_data.to_json, headers)
  17. if res.code == "200"
  18. res_message = "GCM Post Message Success."
  19. return true, res_message
  20. else
  21. res_message = "GCM Post Message Failed."
  22. end
  23. return false, res_message
  24. end
  25. end
gcm_access.rb

 実際に端末にメッセージを送る部分に関しては、GCMサーバがやってくれます。Androidなどのモバイル端末では、モバイル独自の状態によるメッセージ不通(電源が入っていない、ネットワーク途絶など)を考慮する必要がありますが、その辺りはGCMサーバがうまくハンドリングしてくれるので、キューイングや再送処理といった点は特に気にする必要はありません。

 高度なオプションに関しては、「ソフトウェア技術ドキュメントを勝手に翻訳」の記事を参照してください。

アプリを動かすと……

 このようにGCMを利用すれば、任意のタイミングで任意の端末に対してメッセージを送信できます。実際のアプリの挙動は以下の図のようになります。

Android端末XおよびYでデバイスの登録を行う
Android端末Xで「ぺこりする」ボタンを押してあいさつを送信
Android端末Yに通知される

 今回は「あいさつ」をメッセージとして送信しましたが、あいさつ以外でも送信するメッセージによって、オフラインの行動につながるきっかけを与えることができると思います。

 また、本稿では特定のアプリのソースコードを抜粋して紹介していますので、全体の流れがつかみづらいところがあるかもしれません。Android側とサードパーティアプリケーションサーバ側のサンプル実装をしている例を、ブログでも紹介しているので、参考にしてみてください。

Copyright © ITmedia, Inc. All Rights Reserved.

スポンサーからのお知らせPR

Smart & Social 險倅コ九Λ繝ウ繧ュ繝ウ繧ー

譛ャ譌・譛磯俣

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。