GCMの使用を強いられたので,メモです. GCMはサーバからandroidアプリに対して, push通知を送る仕組み・サービスです. 2012/6/26に,公開されたようです. それ以前は,C2DMを使って実装していました. 簡単なサンプルですが,メモとして残しておきます.
こちらに置いています.
http://developer.android.com/google/gcm/index.html,http://www.kotemaru.org/2013/07/28/android-push-message.htmlから,一部引用しています.
仕組み
- Googleのサーバを,GCMサーバ
- 自身のサーバを,自サーバ
と呼ぶことにします.
- はじめにGCMサーバから,端末のID( レジスタID )を取得する.
- レジスタIDを自サーバへ送信する.
- 自サーバから,レジスタIDを設定し,メッセージをGCMサーバへ送る.
- GCMサーバから各端末にメッセージが送信され,push通知される.
準備
まず,https://code.google.com/apis/console/から GoogleAPIを取得します.
- Service一覧から,Google Cloud Messaging for Androidのstatusをonにします.
- API ACCESSから,Create new Server Keyを選択し,API Keyを取得します.
ここで,2つの情報を確認しておきます.
- API Key: 自サーバから,GCMサーバへメッセージを送信する場合に使う. Key for server appsに書かれています.“Jfdsah82…“のようなもの.
- Sender id: アプリから,自サーバへメッセージを送信する場合に使う.URLのprojectの後ろの数値です.“4352…“のようなもの.
1. クライアントの実装
古いですが,Google APIs 10で実装しました. はじめに,Android SDK ManagerからGoogleCloudMessaging for Android Libraryをインストールします.次に,{Android SDKディレクトリ}/extras/google/gcm/gcm-client/dist/gcm-src.jarを,プロジェクトに追加します.(srcディレクトリで右クリック => Archive Fileで追加できます)
マニフェストファイル
マニフェストファイルに以下のようにpermissionを追加します. BEGIN,ENDで囲ったところを追加してください. com.example.comのところはプロジェクトのパッケージ名に変更します.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.gcm_reciever"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="10" />
<!-- BEGIN: for GCM_reciever -->
<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.gcm_reciever.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.gcm_reciever.permission.C2D_MESSAGE" />
<!-- END: for GCM_reciever -->
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.gcm_reciever.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>
<!-- BEGIN: for GCM_reciever -->
<receiver
android:name="com.google.android.gcm.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.gcm_reciever" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="com.example.gcm_reciever"/>
</intent-filter>
</receiver>
<service android:name=".GCMIntentService" />
<!-- END: for GCM_reciever -->
</application>
</manifest>
MainActivity.java
package com.example.gcm_reciever;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import com.google.android.gcm.GCMRegistrar;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
/* 自サーバのURL */
public static final String url = "http:/hogehoge.net/closet/gcm.php";
/* sender id */
public static final String senderIds = "xxxxxxxxxx";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
/* GCMサーバからレジスタIDを取得する.
* 登録済みなら,それを自サーバへ送信する.
* 未登録なら,GCMResistrar.register()でGCMサーバへ登録する.
* (自サーバへは,Registeredのタイミングで送信される)
*/
String regId = GCMRegistrar.getRegistrationId(getApplicationContext());
if(regId.equals("")){
Log.i("GCM", "onCreate call register");
GCMRegistrar.register(getApplicationContext(), senderIds);
}else{
Log.i("GCM", "onCreate call sendMyserver");
sendMyServer(regId);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
protected void onDestroy() {
GCMRegistrar.onDestroy(this);
super.onDestroy();
}
/**
* レジスタIDを自サーバへ送信する.
* @param regId レジスタID
*/
static public void sendMyServer(String regId){
HttpClient client = new DefaultHttpClient();
List parameters = new ArrayList();
parameters.add(new BasicNameValuePair("action", "reg"));
parameters.add(new BasicNameValuePair("regId", regId));
String query = URLEncodedUtils.format(parameters, "UTF-8");
HttpGet get = new HttpGet(url + "?" + query);
Log.i("GCM", "sendMyServer: regId="+regId);
try {
HttpResponse response = client.execute(get);
Log.i("GCM", "sending my server success.");
} catch (ClientProtocolException e) {
e.printStackTrace();
Log.i("GCM", "sending my server failed.");
} catch (IOException e) {
e.printStackTrace();
Log.i("GCM", "sending my server failed.");
}
}
}
GCMIntentService.java
package com.example.gcm_reciever;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
import com.example.gcm_reciever.*;
public class GCMIntentService extends GCMBaseIntentService {
private String tag = "GCM";
public GCMIntentService() {
super(MainActivity.senderIds);
}
/**
* レジスタIDがGCMサーバへ登録されたときに呼ばれる.
* ここで,自サーバへも送信します.
*/
@Override
protected void onRegistered(Context context, String regId) {
Log.i(tag, "onRegistered: regId = " + regId);
MainActivity.sendMyServer(regId);
}
/**
* GCMサーバからメッセージを受信した時によばれる.
*/
@Override
protected void onMessage(Context context, Intent intent) {
Bundle extras = intent.getExtras();
String message = extras.getString("message");
Log.i(tag, "onMessage: msg = " + message);
}
@Override
public void onError(Context context, String errorId) {
Log.i(tag, "onError: " + errorId);
}
@Override
protected boolean onRecoverableError(Context context, String errorId) {
Log.i(tag, "onRecoverableError: " + errorId);
return super.onRecoverableError(context, errorId);
}
@Override
protected void onUnregistered(Context context, String registrationId) {
// TODO Auto-generated method stub
}
}
2. サーバの実装
<?php
require_once "HTTP/Request.php";
if(!ISSET($_GET['action']))
exit(0);
if($_GET['action'] == "reg"){
$fp = fopen("id.txt", "w");
fwrite($fp, $_GET['regId']);
fclose($fp);
print('saved');
}else if(($_GET['action']) == "send"){
$fp = fopen("id.txt", "r");
$regid = fgets($fp);
fclose($fp);
$apikey = "AIza....";
$msg = 'posted from gcm';
$rq = new HTTP_Request("https://android.googleapis.com/gcm/send");
$rq->setMethod(HTTP_REQUEST_METHOD_POST);
$rq->addHeader("Authorization", "key=".$apikey);
$rq->addPostData("registration_id", $regid);
$rq->addPostData("collapse_key", "1");
$rq->addPostData("data.message", $msg);
if (!PEAR::isError($rq->sendRequest())) {
print "\n" . $rq->getResponseBody();
} else {
print "\nError has occurred";
}
}
3. エラーコードとか
いくつかエラーを経験したので,その対処法.
java.lang.IllegalArgumentException: Receiver not registered: com.google.android.gcm.GCMBroadcastReceiver@40673a10
=> GCMへの登録を行う際に,GCMRegistrar.onDestroy(getApplicationContext());のように,contextを修正する.
ACCOUNT_MISSING
=> その端末にGoogleアカウントでログインされていないと発生します.特にエミュレータでは注意が必要です.