Hatena::ブログ(Diary)

azukinohirokiの日記

2012-06-28

Google Cloud Messaging for Android (GCM)を使ってみた

★2014.3.18追記あり

Android 4.1のAPIが公開されましたね!

お仕事でC2DMを使おうとしていた矢先に、

Android Cloud to Device Messaging (C2DM) is deprecated.

http://developer.android.com/guide/google/gcm/c2dm.html

というわけで、C2DMの進化版、GCMを使ってみました。

基本的な使い方はC2DMと似てるので、移行はそんなに難しくなかったです。

一から作るのも、
GCM: Getting Started
ここの手順をなぞればさくっと出来る感じです。
僕はさくっと出来なかったので備忘録かわりに書いておきます。

Sender IDを取得


Google APIs Console page
Google APIs ConsoleGCM serviceをONにします。

API projectがまだない場合はCreate project...します。
プロジェクトを作るとアドレスバー

https://code.google.com/apis/console/#project:4815162342

こんな感じでproject IDが表示されるので#project:の後の数字を控えておきます。
このIDがC2DMのSender IDに該当します。
(上のIDはご本家に書いてあったのコピーしてるので読み替えて下さい)

GCM serviceをONにする為に、

  1. 左側のメニューからServicesを選択
  2. Google Cloud MessagingのトグルをON
  3. 何か言われるのでとりあえずaccept

API Key(=Authorization key)を取得


C2DMの時に使っていたAuthorization keyはここで作ります。

  1. 引き続きAPIs ConsoleAPI Accessを選択し、Create new Server key
  2. IP制限とか必要なければそのままCreate。
  3. Key for server apps (with IP locking)のAPI key:の値をメモ。

これでSender IDとAuthorization keyの作成は終わり。
次はアプリ側を作ります。

アプリ


便利なライブラリをご本家が公開してくれたので、SDK Managerを使って落とします。
Extras > Google Cloud Messaging for Android Library
です。

gcm.jarってのが、YOUR_SDK_ROOT/extras/google/gcm-client/distの中にあるので組み込みます。

お次はちょい面倒なManifestの編集。

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="xx"/>

GCMAndroid 2.2以上でしか使えません。

次に

<permission android:name="my_app_package.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="my_app_package.permission.C2D_MESSAGE" /> 
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

my_app_packageのとこはアプリのパッケージ名を入れる。
この辺はC2DMと同じ。

applicationの中は、こんな感じで。

<application android:icon="@drawable/icon" android:label="@string/app_name">
  <service android:name="my_app_package.GCMIntentService" />

  <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" />
      <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
      <category android:name="my_app_package" />
    </intent-filter>

  </receiver>
</application>

GCMBroadcastReceiverはライブラリの中に入ってて、my_app_package.GCMIntentServiceは自分で用意します。

GCMIntentService


GCMBaseIntentServiceを継承してGCMIntentServiceクラスを作ります。
GCMBaseIntentServiceもライブラリの中にあります。

メッセージのレシーバは
Android開発 C2DMを触ってみよう
を参考にさせてもらいました。

コンストラクタでさっきメモったproject ID(=Sender ID)を親クラスに渡します。

package my_app_package;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;

import com.google.android.gcm.GCMBaseIntentService;

public class GCMIntentService extends GCMBaseIntentService {
	
    public GCMIntentService() {
        super("SENDER_ID");
    }

    @Override
    public void onRegistered(Context context, String registrationId) {
        Log.w("registration id:", registrationId);
        sendMessage("id:" + registrationId);
    }
 
    @Override
    protected void onUnregistered(Context context, String registrationId) {
        sendMessage("C2DM Unregistered");
    }
 
    @Override
    public void onError(Context context, String errorId) {
        sendMessage("err:" + errorId);
    }
 
    @Override
    protected void onMessage(Context context, Intent intent) {
        String str = intent.getStringExtra("message");
        Log.w("message:", str);
        sendMessage(str);
    }
   
    private void sendMessage(String str) {
        //本体側に通知するなりなんなり
    }
}

registrationした時はonRegistered、なにかメッセージが来るとonMessageに飛んできます。
あとは、

onRecoverableError(Context context, String errorId)

なんてのもあるみたい。


レシーバが書けたら本体のActivityをいじります。

import com.google.android.gcm.GCMRegistrar;

して、onCreateの中で

GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
  GCMRegistrar.register(this, "SENDER_ID");
} else {
  Log.v(TAG, "Already registered");
}

ここでもSender IDを使います。
GCMRegistrar.checkDevice()でデバイスが対応しているかどうか、
GCMRegistrar.checkManifest()でマニフェストの設定があってるか見てくれます。
NGの時は例外投げるそうです。

これでアプリを起動すると、registration IDが取れるはずです。
registration IDも後で使うのでメモ。

サーバ


アプリでregistration出来たら、サーバからpushしてあげればメッセージが受け取れます。

本家では便利なライブラリ使ってサーブレット作ってねって言ってましたが
サーブレットっておいしいの?な僕はまた
Android開発 C2DMを触ってみよう
を参考にさせてもらいました。
最初の方で取ったAPI keyとさっきのregistration IDを使います。

<?php

$url = 'https://android.googleapis.com/gcm/send';

$registration_id = 'AAAAAAAAAAAAAAAAAAA'; //registration IDはここ
$message = 'Hello, GCM!!';

$header = array(
  'Content-Type: application/x-www-form-urlencoded;charset=UTF-8',
  'Authorization: key=XXXXXXXXXX', //API keyはここ
);
$post_list = array(
  'registration_id' => $registration_id,
  'collapse_key' => 'update',
  'data.message' => $message,
);
$post = http_build_query($post_list, '&');

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

//CA証明書の検証をしない
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

$ret = curl_exec($ch);
 
var_dump($ret);

?>

このスクリプトを叩くとレシーバのonMessageがコールされます。
成功した時はPHP側で"id=XXXXXXX"とメッセージのIDが表示されます。

投げ先のURLや、GCMサーバの応答などなどは
GCM Architectural Overview
に細かく載ってます。

Interpreting a success responseやInterpreting an error response辺りに
レスポンス周りが載ってるので、上手くいかない時は参考になるかも。


★2014.3.18追記
コメントしていただいた、

//CA証明書の検証をしない
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);

サーバ側のスクリプトに追記しました!

★2016.1.6追記
上記の追記したスクリプト変数名が元のスクリプトと合っていなかったので修正しました!
コメントありがとうございました。

まとめ


はてな記法ちゃんと使えばもっと読みやすくなるんだろうか。。。

anrianri 2012/06/29 11:59 サーバ側からのPush部分でもし何かお分かりになれば教えていただきたいのですが、
同じように記述しても「bool(false) 」が出力されてしまうのは、
どのような可能性がありますでしょうか?

azukinohirokiazukinohiroki 2012/06/29 12:04 サーバ側もPHPも不勉強で細かいことは分からないんですが、Authorization keyがおかしいと同じ結果になりました!

anrianri 2012/06/29 12:48 コメントありがとうございます!
Authorization keyを確認してみます。
試していただいてありがとうございました!!

anrianri 2012/06/29 13:10 できました!!
やはりAuthorization keyが違っていたようです。
とても勉強になりました、ありがとうございます。

azukinohirokiazukinohiroki 2012/06/29 13:19 いえいえ、参考になれば幸いです!
コメントありがとうございました。

giuseppegiuseppe 2012/06/29 17:21 VERY GOOD POST !!! CONGRATULATIONS FROM ITALY !!!!

azukinohirokiazukinohiroki 2012/06/29 17:45 Prego!!

hayamikahayamika 2014/02/26 00:22 とても参考になる内容をありがとうございます。

私もbool(false)が出てしまって、
API keyの確認をしていた所、
しつこく更新ボタンを押していると、
string(38)"id=0:213123・・・・"
と出てメッセージが送られるのですが、
bool(false)ばかり出て、なかなか送れないのは何故なのでしょうか。。。
もしお分かりになればと思い、書込みをさせて頂きました、、

azukinohirokiazukinohiroki 2014/02/26 10:00 コメントありがとうございます^^
更新ボタンということはブラウザでphp叩いてる感じでしょうか。
私は普段使う時はサーバの中からコマンドで叩いてました。
送れたり送れなかったりというのは遭遇したことがないのでちょっと分からないです。。すみません。。

ガミPガミP 2014/03/12 18:37 //CA証明書の検証をしない
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
証明書関係だと思いますが。同じような現象で困ってました。
ただ、これだとhttpsの意味ないですよね。

azukinohirokiazukinohiroki 2014/03/12 19:25 フォローありがとうございます!
なるほど...サーバ側の設定次第なのでしょうか。。

ガミPガミP 2014/03/18 10:15 そうですね。サーバサイドの問題だったらしいです。こちらの記事、コメントが大変参考になりました。ありがとうございます。
twitterフォローさせて頂きました。同じ名前だと思いますのでよろしければお願いします。

azukinohirokiazukinohiroki 2014/03/18 11:07 本文に追記させていただきました!
twitterもありがとうございます^^

nonamenoname 2016/01/04 19:00 curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
の唐突に出てくる$curlって$chですか?

azukinohirokiazukinohiroki 2016/01/04 20:12 その通りです!本文も修正しておきました。
コメントありがとうございました^^;

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元