ラベル GCM の投稿を表示しています。 すべての投稿を表示
ラベル GCM の投稿を表示しています。 すべての投稿を表示

2012年11月22日木曜日

BaaS(Backend as a Service)のプッシュ通知サービス

最近、BaaS(Backend as a Service)っていう言葉をよく聞きます。これは、スマートデバイス(モバイル)アプリケーション向けに、サーバサイド実装の手間を省くため、データベースへの保存・参照、SNSとの連携、プッシュ通知、認証、ファイル保存などの機能を簡単に呼び出すことができるサービスです。(実際はモバイルのみが対象ではないものもあり、本当にモバイル特化の場合は、MBaaSという呼び方をする場合もあるそうです。)これらのサービスにはSDK(開発キット)が提供されておりAPIをコールするだけで呼び出すことができたり、RESTでアクセスする機能が提供されています。
今回は、そのBaaSの中の1つのサービス「プッシュ通知」機能について、各ベンダーごとにどのようなサービスを提供しているのかを見ていきたいと思います。

その前に、プッシュ配信の仕組みについては、ほとんどのベンダーがApple、Google、Microsoftが提供するサービス「APNS(Apple Push Notification Service)」、「GCM(Google Cloud Messaging for Android)」、もしくはMicrosoftの「WNS(Windows Push Notification Service)」をベースに作っています。簡単に言えばラップしているだけのサービスなので、以下のようにBaaSベンダーはプッシュされたメッセージを各ベンダーごとに振り分けるというようなサーバを用意しているというイメージになります。


このことから、以下のページを参考にして、自分で実装することもできます。

ただ、簡単になっていますので、BaaSを使った方が便利ではありますね。

では、各BaaSベンダーのサービスです。

Microsoft Mobile Services
上記でも軽く触れました名前の通りのMicrosoftがAzure上で提供する「Windows Push Notification Service (WNS)」というサービスです。そのため以下のようにWindowsがサポート対象ですが、将来的にはAndroidやiOSにも対応するようです。
・プラットフォーム: Windows Store, Windows Phone
・言語: C#など

ACS(Appcelerator Cloud Services)
モバイル開発ツールで有名な「Titanium Mobile」を作っている会社「Appcelerator」がCocoafishという会社を買収して、ACSという名称で提供しています。
・プラットフォーム: Android or iOS
・言語: JavaScript, REST, AS3, Java for Android

Buddy
ここのサービスは、他とは違いAndroidとiOSに加え、Windows系もサポートしています。内部的には上記のWindows Push Notification Service(WNS)を使っているようです。
・プラットフォーム: Android, iOS, Windows Store, Windows Phone 
・言語: Java, Objective-C, C#

appiaries(国産)
国産のBaaSのようです。ただ、プッシュ配信は、まだ未提供のようです。将来的にはAndroid/iOS対応予定とのこと。

Parse
・プラットフォーム: Android, iOS
・言語: REST, JavaScript, Java for Android, Objective-C for iOS

StackMob

・プラットフォーム: Android, iOS
・言語: Java for Android, Objective-C for iOS, REST
   
Kinvey
http://www.kinvey.com/
・プラットフォーム: Android, iOS
・言語: Java for Android, Objective-C for iOS

その他、ApplicasaQuickbloxSencha.ioなどの有名なBaaSがありますが、あまり変わらなそうなので、とりあえずこれらは未調査。


上記サービスは基本的に、APNS、GCM、WNSをベースに作られていますが、プッシュ配信と言えば、HTML5のWebSocketが有名です。そこで、WebSocketをベースにプッシュ配信サービスを提供しているベンダーを少し挙げてみます。

pusher
PHP, Ruby, JavaScriptなどWebSocket ClientのAPIがあればアクセス可能

Kaazing
http://kaazing.com/
プッシュ通知サービスは、Kaazing WebSocket Gateway (HTML5 Edition)と呼ばれるようです。こちらもWebSocket Clientのライブラリからアクセスできますが、あらかじめ、Java, JavaScript, .Net, SilverLight, AIR, Flex, Flashなどのライブラリが用意されているようです。また、WebSocket以外にも、JMS, AMQP, XMPPもサポートしています。(ただし、WebSocketほどメジャーではないですが。)

以上がプッシュ通知のサービスですが、今後はBuddyなどのようにAPNS、GCM、WNSを連携するのみでなく、さらにWebSocketなどもサポートされていくのではないかと思います。

2012年10月23日火曜日

Google Cloud Messaging for Android(GCM)を試してみる

モバイルアプリも流行ってきたので、さらにサーバプッシュの必要性が高くなってくると思い、メンバーと一緒にいろいろ試していってみようと思います。(Google Cloud Messaging、Apple Push Notification Service、WebSocketなど)

まずは、Google Cloud Messaging for Android(以下GCM)について試してみました。(ちょっと前までは、C2DMと呼ばれていたものです。)

動作は以下の図のように、Androidデバイスからregister idというものを生成及び登録し、その後サーバなどから登録されたデバイスに対してプッシュすることで、メッセージをリアルタイムに通知するという単純なものです。



実装については、基本的にはこちらのページ通りにやっていけばできると思いますが、ポイントとハマったところをまとめていければと思っています。また、サーバサイドのプッシュをPHPでやっている記事は多いですが、チュートリアルにあるサーブレットでやっている人が少なそうなので、今回はJavaサーブレットを使ってみたいと思います。

環境
Apache Tomcat 7
Android 4.1

API KeyとSENDER ID取得
https://code.google.com/apis/consoleからAPI KeySENDER IDを取得します。簡単なので詳しくは、http://developer.android.com/guide/google/gcm/gs.htmlを参照ください。

サーブレット作成
まずは、サーバサイドの仕組みを作るため、プロジェクト(Eclipse for JavaEEを使った場合は、Dynamic Web Project)を作成します。

AndroidをEclipseで開発するために、Android Development Tools(ADT)pluginをインストールして、Android SDK Managerで「Extras」⇒「Google Cloud Messaging for Android」を選択しGCMをダウンロードします。そのダウンロードしたディレクトリの中のgcm/gcm-server/dist/gcm-server.jarをプロジェクトにコピーします。(WEB-INF/libなどへ)
ただ、このjarファイルだけですと、java.lang.ClassNotFoundExceptionが発生します。
java.lang.ClassNotFoundException: org.json.simple.parser.ParseException
これは、gcm/gcm-server/lib/json_simple-1.1.jarがないため発生してるようなので、同様にコピーします。


では、早速サーバサイドのコードですが、3つのサーブレットを作ることとします。1つはregister IDの登録、2つ目はregister IDの登録解除、最後はメッセージ送信。

①. register ID登録サーブレット
Androidから受け取ったregister IDを適当なデータベースなどに登録しておきます。ここはデータベースではなくてもファイルでもキャッシュでも何でもいいです。

②. register ID登録解除サーブレット
単純に①で登録したものを削除します。

③. メッセージ送信サーブレット(サーバからクライアントへプッシュ)
Message.Builder builder = new Message.Builder().delayWhileIdle(true);
builder.addData("message", "Hello"); // 送信するデータ
Message message = builder.build();

String myAppKey = <API Key>
List<String> devices =<①で登録されたregister IDのList<String>>
Sender sender = new Sender(myAppKey);
MulticastResult result = sender.sendNoRetry(message, devices);


これでdevicesに対して、メッセージを送信できます。しかし、メッセージ送信時に以下のエラーが発生しました・・・。

javax.net.ssl.SSLHandshakeException:
   sun.security.validator.ValidatorException: PKIX path building failed:
   sun.security.provider.certpath.SunCertPathBuilderException:
   unable to find valid certification path to requested target

Caused by: sun.security.validator.ValidatorException:
   PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
   unable to find valid certification path to requested target

Caused by: sun.security.provider.certpath.SunCertPathBuilderException:
   unable to find valid certification path to requested target

これはSSL通信を行うために証明書が必要みたいなので、jssecacertsファイルを作成し、$JAVA_HOME/lib/securityにコピーするで解決できました。(keytoolを使うのもいいですが、私は簡単そうだったのでInstallCert.javaを使いました。)

ちなみに以下のコマンドでjssecacertsファイルを作成。
> java InstallCert android.googleapis.com

Androidプロジェクト作成
Androidプロジェクトを作成し、上記でダウンロードしてきたgcm/gcm-client/dist/gcm.jarをAndroidプロジェクトにコピーします。

次にActivityとServiceを作成します。

AndroidManifest.xml

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

    <application
        <activity
            android:name=".GCMActivity"
            android:configChanges="orientation|keyboardHidden"
            android:windowSoftInputMode="adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <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="com.scratch" />
                </intent-filter>
        </receiver>
        <service android:name=".GCMIntentService" />
    </application>

Serviceクラス(GCMIntentService)
package com.scratch;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.google.android.gcm.GCMBaseIntentService;

public class GCMIntentService extends GCMBaseIntentService {

        static String SENDER_ID = <SENDER ID>;
        final private String TAG = getClass().getName();

        @Override
        protected void onError(Context context, String regId) {
                Log.v(TAG, "error " + regId);
        }

        @Override
        protected void onMessage(Context context, Intent intent) {
                Log.v(TAG, "Received message");
                Log.v(TAG, intent.getStringExtra("message")); //サーバからプッシュされたデータの受け取り
        }

        @Override
        protected void onRegistered(Context context, String regId) {
                Log.v(TAG, "registered regId=" + regId);
                // ここでサーブレット①にリクエストしてregister idを登録。
        }

        @Override
        protected void onUnregistered(Context context, String regId) {
                Log.v(TAG, "unregistered " + regId);
                // ここでサーブレット②にリクエストしてregister idを削除。
        }

        public GCMIntentService() {
                super(SENDER_ID); // superでSENDER IDを指定するようです。
                Log.v(TAG, "create GCMIntentService()");
        }
}

Activityクラス(GCMActivity)
package com.scratch;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gcm.GCMRegistrar;

public class GCMActivity extends Activity {

        final private String TAG = getClass().getName();

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

                // check if this device supports GCM.
                GCMRegistrar.checkDevice(this);

                // check if it's right manifest.
                GCMRegistrar.checkManifest(this);
                final String regId = GCMRegistrar.getRegistrationId(this);
                Log.v(TAG, "regId=" + regId);
                if (regId.equals("")) {
                       // GCMIntentService.onRegisteredがコールされる。
                       GCMRegistrar.register(this, GCMIntentService.SENDER_ID); 
                } else {
                        Log.v(TAG, "Already exists " + regId);
                }
        }
}

ちなみに、unregisterはをするにはGCMRegister.unregisterメソッドを使い、これが実行されるとGCMIntentService.onUnregisteredが呼ばれます。

これでAndroidでサーバプッシュができるはずです。一度お試しを!(詰まったらコメントください。)

次回は、AppleのApple Push Notification Service(APNS)についても試していこうと思います。