忍者ブログ

アンドロイドのあれこれ

Push通知、GCM(Google Cloud Messaging)を試す。複数端末に送信
gcm-logo.pngAndroidのPush通知でメッセージをアプリに送信する仕組みは以前C2DM(Android Cloud to Device Messaging Framework)というものでしたが、2012年6月26日の時点から廃止されました。その代わりにGCM(Google Cloud Messaging)というものが推奨されています。

Google Cloud Messaging for Android
http://developer.android.com/google/gcm/index.html


GCMのSender ID、APIとサンプルはAndroid SDK ManagerのExtras > Google Cloud Messaging for Android Libraryからダウンロードできます。
サンプルは
(AndrodSDKディレクトリー)/extras/google/gcm/gcm-client
にあります。
Google Cloud Messaging for Android Library

API keyの登録やサンプルコードを動かすには以下の記事を参考してみてください。
http://d.hatena.ne.jp/azukinohiroki/20120628/1340868610
http://d.hatena.ne.jp/good-speed/20120702/1341223850

今日はGCMのサンプルコードを参考しサーバー側で複数端末にpush送信する簡単なものを紹介します。

- まずはアプリ側、メッセージを受け取る、受け取らないのregister、unregisterなどの設定
//MainActivity.java
public class MainActivity extends Activity {

private TextView mDisplay;
private Button btnRegister;
private Button btnUnRegister;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// デバイスが適切な依存関係を持っていることを確認します。
GCMRegistrar.checkDevice(this);
// マニフェストが適切に設定されていることを確認します - この行をコメントアウト
// アプリを開発しながら準備ができたら、それをアンコメントします。
GCMRegistrar.checkManifest(this);

setContentView(R.layout.activity_main);
registerReceiver(mHandleMessageReceiver,
new IntentFilter(DISPLAY_MESSAGE_ACTION));

mDisplay = (TextView)findViewById(R.id.display);
btnRegister = (Button)findViewById(R.id.register);
btnUnRegister = (Button)findViewById(R.id.unregister);

final String regId = GCMRegistrar.getRegistrationId(this);

btnRegister.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
//GCMの登録
GCMRegistrar.register(MainActivity.this, SENDER_ID);
}
});

btnUnRegister.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
//GCMの登録解除
GCMRegistrar.unregister(MainActivity.this);
}
});
}

private final BroadcastReceiver mHandleMessageReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//pushから受け取ったメッセージを表示
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
mDisplay.append(newMessage + "\n");
}
};

@Override
protected void onDestroy() {
unregisterReceiver(mHandleMessageReceiver);
GCMRegistrar.onDestroy(this);

super.onDestroy();
}
}

//CommonUtils.java
public final class CommonUtils {

static final String SERVER_URL = "http://example.com/android_gcm/";

static final String SENDER_ID = "xxxxx";

static final String DISPLAY_MESSAGE_ACTION =
"com.example.demogcm.DISPLAY_MESSAGE";

static final String EXTRA_MESSAGE = "message";

static void displayMessage(Context context, String message) {
Intent intent = new Intent(DISPLAY_MESSAGE_ACTION);
intent.putExtra(EXTRA_MESSAGE, message);
context.sendBroadcast(intent);
}
}

//ServerUtils.java
public final class ServerUtils {

static boolean register(final Context context, final String regId) {
String serverUrl = SERVER_URL + "/register.php";
Map<String, String> params = new HashMap<String, String>();
params.put("regId", regId);
try {
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, true);
return true;
} catch (IOException e){
}

return false;
}

static void unregister(final Context context, final String regId) {
String serverUrl = SERVER_URL + "/unregister.php";
Map<String, String> params = new HashMap<String, String>();
params.put("regId", regId);
try {
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, false);
} catch (IOException e) {
}
}

/**
* regIdの登録、登録解除をサーバーへ送信
*/
private static void post(String endpoint, Map params)
throws IOException {
URL url;
try {
url = new URL(endpoint);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("invalid url: " + endpoint);
}
StringBuilder bodyBuilder = new StringBuilder();
Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
// constructs the POST body using the parameters
while (iterator.hasNext()) {
Entry param = iterator.next();
bodyBuilder.append(param.getKey()).append('=')
.append(param.getValue());
if (iterator.hasNext()) {
bodyBuilder.append('&');
}
}
String body = bodyBuilder.toString();
Log.v("TEST", "Posting '" + body + "' to " + url);
byte[] bytes = body.getBytes();
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
// post the request
OutputStream out = conn.getOutputStream();
out.write(bytes);
out.close();
// handle the response
int status = conn.getResponseCode();
if (status != 200) {
throw new IOException("Post failed with error code " + status);
}
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}

//GCMIntentService
public class GCMIntentService extends GCMBaseIntentService {

public GCMIntentService() {
super(SENDER_ID);
}

@Override
protected void onError(Context context, String errorId) {
// TODO Auto-generated method stub
}

@Override
protected void onMessage(Context context, Intent intent) {
Log.v("TEST", "onMessage: Received some message");

Bundle extras = intent.getExtras();
String result = extras.getString(EXTRA_MESSAGE);
displayMessage(context, result);
showNotification(context, result);
}

@Override
protected void onRegistered(Context context, String regId) {
Log.v("TEST", "regId = " + regId);
ServerUtils.register(context, regId);
}

@Override
protected void onUnregistered(Context context, String regId) {
if (GCMRegistrar.isRegisteredOnServer(context)) {
ServerUtils.unregister(context, regId);
}
}

/**
* サーバーからメッセージを受け取ったときの動作
* 通知を出したり、音やバイブレーション動作など
*/
private static void showNotification(Context context, String message) {
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(R.drawable.ic_notification, message, when);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent =
PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}
}


AndroidManifest.xmlに必要な権限やbroadcastreceiverの追加
...
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
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="(app package name)" />
</intent-filter>
</receiver>
...

- サーバー側の処理
端末のregIdはベタで簡単な流れを理解するためにファイルとして保存していますが、実際にはデーターに保存・削除するシステムが必要です。
//register.php
<?php
if (!isset($_POST['regId'])) exit;

$registerDir = dirname(__FILE__) . "/htdocs";
$regId = $_POST['regId'];
$filename = md5($regId). ".txt";

//regIdデータをファイルとして保存
file_put_contents($registerDir."/{$filename}", $regId);

//unregister.php
<?php
if (!isset($_POST['regId'])) exit;

$registerDir = dirname(__FILE__) . "/htdocs";
$regId = $_POST['regId'];
$targetFilename = md5($regId). ".txt";

$fileList = scandir($registerDir);

for ($i = 2; $i < count($fileList); $i++) {
$filename = $fileList[$i];
if ($filename === $targetFilename) {
//対象のファイル削除
unlink($registerDir."/{$filename}");
exit;
}
}

//sender.php
<?php
$registerDir = dirname(__FILE__) . "/htdocs";

//push送信先
$url = 'https://android.googleapis.com/gcm/send';
//apiKey
$apiKey = "xxxxxxxxxx";
//regIdを保存しているファイル一覧
$fileList = scandir($registerDir);

//API key
$header = array(
'Content-Type: application/x-www-form-urlencoded;charset=UTF-8',
"Authorization: key={$apiKey}",
);

//すべての端末にpush送信
for ($i = 2; $i < count($fileList); $i++) {
$filename = $fileList[$i];

//regIDを取得
$regId = file_get_contents($registerDir . "/{$filename}");

//送信したいメッセージ
$message = 'キタ━━━━(゚∀゚)━━━━!!';

$postParam = array(
'registration_id' => $regId,
'collapse_key' => 'update',
'data.message' => $message,
);
$post = http_build_query($postParam, '&');

$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_FAILONERROR, 1);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
$ret = curl_exec($curl);
}

実装イメージ
Push通知、GCM(Google Cloud Messaging)を試す。複数端末に送信
COMMENT
NAME
TITLE
MAIL (非公開)
URL
EMOJI
Vodafone絵文字 i-mode絵文字 Ezweb絵文字
COMMENT
PASS (コメント編集に必須です)
SECRET
管理人のみ閲覧できます
 
PR
© Android Advent
powered by 忍者ツールズ / 忍者ブログ / [PR]