この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
今回の記事は先日公開した記事の続編です。AWS SDK for Ruby を使って Amazon SNS に Publish して、Android 端末で Push 通知を受け取りたいと思います!
GCM (Google Cloud Messaging) に登録する
こちらでも簡単に説明していますが、同様に手順を確認しながら進めましょう。まずは Google Developers Console にアクセスし「Create Project」をクリックします。
適当な名前で作ります。
右のメニューから APIs & auth を選択し、「Google Cloud Messaging for Android」を探し出し、ON にします。
次に Credentials を選択し「CREATE NEW KEY」をクリックします。
どこで使うキーか聞かれるので「Server key」を選択します。
次にアクセスを許可する IP の設定が表示されますが、今回は特に使わないのでそのままで。
できあがったら API key をメモしておきます。あと URL にある ProjectID もあとで使うのでメモしておきます。これで GCM の準備完了!
Amazon SNS の App を作成する
次に Amazon SNS の出番です。基本的にこちらと同じように App を作成するだけです。まずは「Add a New App」をクリックします。
次に ApplicationName を適当な名前にし、Push Platform を GCM に変更、そして API Key にメモっておいた API Key を入力して「Add New App」をクリックします。
これでおしまいです。PlatformApplicationArn というものが生成されると思うので、メモしておいてください。また次項で Publish 用のサーバーを作りますが、AWS へのアクセスが必要になるのでついでに IAM User を作っておくと良いでしょう。
Publish 用のサーバーを Heroku に構築する
サーバーサイドの実装は以下の記事をベースにしているので、こちらをご覧いただいてから読んでいただければと思います。
[Ruby] Sinatra + PostgreSQL + Unicorn な Web サーバーを Heroku に構築する
まずは gem に aws-sdk を追加します。
vim Gemfile
Gemfile
...
gem 'aws-sdk'
...
bundle install
次に AWS の設定ファイルを突っ込む aws.yml を作成します。
vim aws.yml
aws.yml
development:
access_key_id: YOUR_ACCESS_KEY_ID
secret_access_key: YOUR_SECRET_ACCESS_KEY
region: YOUR_REGION
test:
access_key_id: YOUR_ACCESS_KEY_ID
secret_access_key: YOUR_SECRET_ACCESS_KEY
region: YOUR_REGION
production:
access_key_id: YOUR_ACCESS_KEY_ID
secret_access_key: YOUR_SECRET_ACCESS_KEY
region: YOUR_REGION
次に main.rb を編集します。AWS の設定を読み込むところと Publish 用の API を新たに用意しておきましょう。リクエストパラメータは message とし、好きなメッセージを送れるようにします。platform_application_arn は先ほど SNS の App 作成時にメモした PlatformApplicationArn を入れてください。
vim main.rb
main.rb
require 'sinatra' require 'sinatra/base' require 'active_record' require 'aws-sdk'
ActiveRecord::Base.configurations = YAML.load_file('database.yml') ActiveRecord::Base.establish_connection(ENV['RACK_ENV'])
# AWS の設定ファイルを読み込む AWS.config(YAML.load_file('aws.yml')[ENV['RACK_ENV']])
class User < ActiveRecord::Base end class MainApp < Sinatra::Base get '/' do User.all.to_json end post '/' do user = User.new user.registration_id = params[:registration_id] user.save! status 202 end # Publish API post '/publish' do sns = AWS::SNS.new client = sns.client # とりあえずUserの先頭にPublish response = client.create_platform_endpoint( platform_application_arn: 'YOUR_APP_ARN', token: User.first.registration_id ) endpoint_arn = response[:endpoint_arn] client.publish( target_arn: endpoint_arn, message: params[:message] ) end end [/ruby]
これでOKです。あとは Heroku にデプロイしてサーバーの準備完了です。
git add .
git commit -m 'added amazon sns mobile push'
git push heroku master
Subscribe する Android アプリを作成する
次に Subscribe する Android アプリを作成します。こちらと同じように実装します。Google Play Services のライブラリは最新をインストールしておいてください。まず適当なプロジェクトを作成し AndroidManifest.xml を編集します。
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.samplesubscriber"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="com.example.samplesubscriber.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.samplesubscriber.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.samplesubscriber.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.example.samplesubscriber" />
</intent-filter>
</receiver>
<service android:name=".GcmIntentService" />
</application>
</manifest>
次に IntentService と BroadcastReceiver を実装していきます。Subscribe したら GCMIntentService の onHandleIntent() が呼ばれるので、そこで Notification 通知を表示するように実装します。GCMBroadcastReceiver は WakefulBroadcastReceiver というクラスを継承していますが、このクラスは WakeLock 中に Service を起動することができる startWakefulService() という便利なメソッドがあるのでこれを使います。
GCMIntentService.java
package com.example.samplesubscriber;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
public class GcmIntentService extends IntentService {
private static final String TAG = "GcmIntentService";
public GcmIntentService() {
super("GcmIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) {
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
Log.d(TAG, "messageType: " + messageType + ",body:" + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
Log.d(TAG, "messageType: " + messageType + ",body:" + extras.toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
Log.d(TAG, "messageType: " + messageType + ",body:" + extras.toString());
// Notificationで通知
sendNotification(extras.getString("default"));
}
}
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
private void sendNotification(String message) {
NotificationManager manager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent =
PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("GCM Notification")
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setContentText(message);
mBuilder.setContentIntent(contentIntent);
manager.notify(0, mBuilder.build());
}
}
GCMBroadcastReceiver.java
package com.example.samplesubscriber;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ComponentName comp =
new ComponentName(context.getPackageName(), GcmIntentService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
ここまでできたら、あとは MainActivity で GCM に登録する処理を実装して終わりです。登録には GoogleCloudMessaging#register() を使います。引数には先ほどメモった GCM の ProjectID (SenderID) を渡し、登録が成功すると registrationId が渡されます。この registrationId を Heroku に構築するサーバーに対してリクエストして、データベースに登録します。
MainActivity.java
package com.example.samplesubscriber;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
public class MainActivity extends Activity {
/** Logcat出力用タグ. */
private static final String TAG = MainActivity.class.getSimpleName();
/** Google Cloud Messagingオブジェクト. */
private GoogleCloudMessaging mGcm;
/** コンテキスト. */
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = getApplicationContext();
mGcm = GoogleCloudMessaging.getInstance(this);
registerInBackground();
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
String registrationId = "";
try {
// GCM に登録して registrationID を受け取る
if (mGcm == null) {
mGcm = GoogleCloudMessaging.getInstance(mContext);
}
// 自分の ProjectID (SenderID) を入れる
registrationId = mGcm.register("YOUR_SENDER_ID");
Log.d(TAG, "Device registered, registration ID=" + registrationId);
// registrationID をサーバーに登録する
registerId(registrationId);
} catch (IOException ex) {
Log.e(TAG, "Error :" + ex.getMessage());
}
return registrationId;
}
@Override
protected void onPostExecute(String msg) {
}
}.execute(null, null, null);
}
private void registerId(String registrationId) {
Log.e(TAG, "POST処理開始");
// URL
URI url = null;
try {
// 自分の Heroku サーバーの URL を入れる
url = new URI("http://YOUR_APP_NAME.herokuapp.com/");
} catch (URISyntaxException e) {
Log.e(TAG, e.toString());
}
// POSTパラメータ付きでPOSTリクエストを構築
HttpPost request = new HttpPost(url);
List<NameValuePair> post_params = new ArrayList<NameValuePair>();
post_params.add(new BasicNameValuePair("registration_id", registrationId));
try {
// 送信パラメータのエンコードを指定
request.setEntity(new UrlEncodedFormEntity(post_params, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// POSTリクエストを実行
DefaultHttpClient httpClient = new DefaultHttpClient();
try {
String ret = httpClient.execute(request, new ResponseHandler<String>() {
@Override
public String handleResponse(HttpResponse response) throws IOException {
Log.d(TAG, "レスポンスコード:" + response.getStatusLine().getStatusCode());
// 正常に受信できた場合は200系
switch (response.getStatusLine().getStatusCode()) {
case HttpStatus.SC_OK:
case HttpStatus.SC_CREATED:
case HttpStatus.SC_ACCEPTED:
Log.d(TAG, "レスポンス取得に成功");
// レスポンスデータをエンコード済みの文字列として取得する
return EntityUtils.toString(response.getEntity(), "UTF-8");
case HttpStatus.SC_NOT_FOUND:
Log.e(TAG, "データが存在しない");
return null;
default:
Log.e(TAG, "通信エラー");
return null;
}
}
});
Log.d(TAG, "Body:" + ret);
} catch (IOException e) {
Log.e(TAG, "通信に失敗:" + e.toString());
} finally {
// shutdownすると通信できなくなる
httpClient.getConnectionManager().shutdown();
}
}
}
これでクライアントアプリの完成です!
Push 通知を受け取ってみる!
それでは Push 通知を送ってみましょう。Heroku アプリの /publish にリクエストパラメータの message を適当につけて POST すると、クライアントアプリをインストールしている端末に Push 通知が届くはずです!
まとめ
ということで Push 通知を受け取るところまで実装してみました。新年の挨拶は年賀状からメールへと変わっていきましたが、その次は自分でサービス作って Push 通知を送る時代が…来る…(来ないw)
ということで2013年もお世話になりました。良いお年を!