
読者の皆さん、Habrahabrこんにちは!
この投稿では、GoogleのC2DMサービスに注意を払い、このサービスのサポートをAndroidアプリケーションに実装する方法を伝えたいと思います。 C2DMは、Androidデバイスにインストールされたアプリケーションにメッセージを送信するためのAPIを提供する特別なサービスです。 このサービスの使用は、必要に応じて、システムに登録されているが現在アクティブではないユーザーアプリケーションにメッセージを送信するために不可欠な方法です。
C2DMはAndroidプラットフォームの基本的な機能の1つですが、RuNetにはC2DMに関する情報がほとんどありません。 この状況を変えようとする試みは、この投稿のタスクの1つです。
簡単に、簡単なクライアントアプリケーションとサーバーアプリケーションを作成する方法、いくつかの「落とし穴」を示す方法、およびサンプルコードへのリンクを提供します。
クラウドからデバイスへのメッセージング
しかし、今のところ、少しの理論。 上で書いたように、C2DMはユーザーアプリケーションからAndroidアプリケーションへのメッセージ配信サービスです 。詳細についてはこちらをご覧ください 。 つまり これは、Appleの観点から言えば、プッシュ通知の一種です。 インタラクションの一般的なスキームは、インターネットにある次の図に示されています。

3つの主要部分が図から見えます:
- C2DMサービス。 メッセージを配信するためのGoogleのクラウド。 デバイスおよび認証の登録/再登録だけでなく。
- クライアント部分。 メッセージを受け入れるAndroidアプリケーション。
- サーバー部分。 メッセージを送信するクライアントアプリケーション(Googleの用語では「サードパーティサーバー」)。
- AndroidアプリケーションはC2DMに登録されているため、メッセージを受信する準備ができたことを通知し、 登録IDを受け取ります 。 登録IDは、サーバー部分が受信者にメッセージを送信できることを知っている、デバイスの一意の識別子です。 登録時には、Googleアカウントの名前を指定する必要がありますが、これについては後でクライアント側について検討するときに記述します。
- アプリケーションは、サーバーの登録IDを送信して、メッセージを送信できるユーザーを認識します。
- サーバー部分は、C2DMに登録するときにAndroidアプリケーションと同じアカウントを使用してGoogleサーバーで認証され、 認証トークンを受け取ります。 Googleのサービスでの承認と認証の詳細については、 こちらをご覧ください 。
- 登録IDと認証トークンを知っているサーバーは、Androidアプリケーションにメッセージを送信します。
クライアント部
C2DM Androidを使用するには、デバイスが次の要件を満たしている必要があります。
- Android 2.2以降のバージョンである必要があります。 以前のバージョンでは、C2DMはサポートされていません!
- 動作中のGoogleアカウントが必要です。 Androidマーケットをお持ちの場合は、お持ちです。

注:Android 2.2がない場合は、エミュレーターを使用できますが、標準のSDKプラットフォームではなく、Google API(API 8以上)を使用する必要があります。 標準SDKでのGoogleアカウントのサポートはありません。
アプリケーションを作成したら、パッケージ名(パッケージ名)を指定する必要がある特別なサイトに登録する必要があります。最も重要なのは、メッセージの送信に使用されるGoogleのメールアカウントです。 登録時にクライアントで、認証時にサーバーで使用するのはこのメールです。 確認メールを送信する必要があります。この手紙の後、指定されたアカウントを使用してC2DMを操作できます。
次に、「Hello、World!」アプリケーションの変更に進みます。 登録/登録解除、そしてもちろんメッセージの受信を担当するコードの実装が必要です。 初めて自分で書くのは本当に難しい すべてのニュアンスを考慮することは難しいため、 例としてGoogle自体に親切に提供するコードを使用します。 クライアント側でC2DMを操作するための既成のクラスセットをsvn storageからダウンロードします。 そして、それらをプロジェクトの「 src \ com \ google \ android \ c2dm 」ディレクトリに追加します。 次のようになります。

次に、抽象クラスC2DMBaseReceiverのメソッドを実装する必要があります。このため、呼び出しをログに記録するだけでクラスC2DMReceiverを記述し、Mainの隣に配置します。 C2DMReceiverクラスのコンテンツ:
- パッケージ com.home.c2dmtest ;
- import com.google.android.c2dm.C2DMBaseReceiver ;
- import android.app.Notification ;
- import android.app.NotificationManager ;
- import android.app.PendingIntent ;
- import android.content.Context ;
- import android.content.Intent ;
- import android.util.Log ;
- パブリック クラス C2DMReceiver は C2DMBaseReceiverを拡張します{
- パブリック C2DMReceiver ( ) {
- super ( "<yourmail> @ gmail.com" ) ;
- }
- @ オーバーライド
- public void onRegistered ( コンテキストコンテキスト、 文字列 registrationId ) {
- ログ w ( "onRegistered" 、registrationId ) ;
- }
- @ オーバーライド
- public void onUnregistered ( コンテキストコンテキスト) {
- ログ w ( "onUnregistered" 、 "" ) ;
- }
- @ オーバーライド
- public void onError ( コンテキストコンテキスト、 文字列 errorId ) {
- ログ w ( "onError" 、errorId ) ;
- }
- @ オーバーライド
- protected void onMessage ( コンテキストコンテキスト、インテントインテント) {
- ログ w ( "onMessage" 、 "" ) ;
- }
- }
しかし、アプリケーションはまだC2DMで動作する準備ができていません。 必要な権利が設定されておらず、BroadcastReceiverが登録されていません。 これを修正するには、AndroidManifest.xmlを変更する必要があります 。その方法については、 こちらをご覧ください 。 私の例では、ファイルは次のようになります。
- <?xml version = "1.0" encoding = "utf-8" ?>
- <マニフェスト xmlns:android = " schemas.android.com/apk/res/android"
- android:versionCode = "1"
- android:versionName = "1.0" package = "com.home.c2dmtest" >
- <アプリケーション
- android:debuggable = "true"
- android:label = "@ string / app_name" >
- <アクティビティ android:name = "Main"
- android:label = "@ string / app_name"
- android:theme = "@android:style / Theme.NoTitleBar" >
- <意図フィルター>
- <action android:name = "android.intent.action.MAIN" />
- <category android:name = "android.intent.category.LAUNCHER" />
- </ intent-filter >
- </アクティビティ>
- <service android:name = ".C2DMReceiver" />
- <受信機
- android:name = "com.google.android.c2dm.C2DMBroadcastReceiver"
- android:permission = "com.google.android.c2dm.permission.SEND" >
- <意図フィルター>
- <action android:name = "com.google.android.c2dm.intent.RECEIVE" />
- <category android:name = "com.home.c2dmtest" />
- </ intent-filter >
- <意図フィルター>
- <action android:name = "com.google.android.c2dm.intent.REGISTRATION" />
- <category android:name = "com.home.c2dmtest" />
- </ intent-filter >
- </受信機>
- </アプリケーション>
- <uses-sdk android:minSdkVersion = "8" />
- <許可
- android:name = "com.home.c2dmtest.permission.C2D_MESSAGE"
- android:protectionLevel = "signature" />
- <uses-permission android:name = "com.home.c2dmtest.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.WAKE_LOCK" />
- </マニフェスト>
注:C2DMを使用するために正しいandroid.permission.WAKE_LOCKは必要ありませんが、後で必要になります。このファイルを2回リストしないように、事前に追加することにしました。
これで、デバイスをC2DMに登録できます。そのために、OnCreateで次のコードを呼び出します。
C2DMessaging。 register ( this 、 "<yourmail> @ gmail.com" ) ;
すべてがうまくいった場合、数秒後に新しいRegestration IDがC2DMReceiverクラスのonRegisteredメソッドに分類されます。 これが発生しない場合は、LogCatのログでエラーを確認する必要があります。
メソッドを呼び出すことで登録解除できます:
C2DMessaging。 登録解除 ( これ ) ;
現在の登録IDを取得:
文字列 ID = C2DMessaging。 getRegistrationId ( これ ) ;
登録IDはいつでも変更できます。 Googleは新しい意味を送信できるため、この状況に対処できる必要があります。 この例では、このためにすべてがすでに行われています;このメカニズムの実装は、クラスC2DMBaseReceiverのメソッドhandleRegistrationにあります。
最終的なプロジェクトは次のようになります。

ここで、プロジェクトをより視覚的にし、メッセージの処理を拡張する必要があります。 新しいメッセージが到着すると通知が表示され、選択されると、アプリケーションはサーバーによって指定されたテキストで開始されます。
OK、これのために、onMessageコードを次のように変更します。
- @ オーバーライド
- protected void onMessage ( コンテキストコンテキスト、Intent receiveIntent )
- {
- 文字列データ= receiveIntent。 getStringExtra ( "message" ) ;
- if ( data ! = null )
- {
- ログ w ( 「C2DMReceiver」 、データ) ;
- Intent intent = new Intent ( this 、Main。Class ) ;
- 意図。 putExtra ( "メッセージ" 、データ) ;
- NotificationManager mManager = ( NotificationManager )
- getSystemService ( Context.NOTIFICATION_SERVICE ) ;
- Notification notification = new Notification ( android。R . Drawable。Ic_dialog_info 、
- 「私のC2DMメッセージ」 、 システム 。 currentTimeMillis ( ) ) ;
- 通知。 setLatestEventInfo (コンテキスト、 「アプリ名」 、 「C2DM通知」 、
- PendingIntent。 getActivity ( this。getBaseContext ( ) 、 0 、
- インテント、PendingIntent。 FLAG_CANCEL_CURRENT ) ) ;
- mManager。 通知 ( 0 、通知) ;
- }
- }
messageは、サーバーが送信するメッセージの識別子です。 これを新しいIntentに追加して、後でMainで取得できるようにします。
Mainを変更して、送信されたメッセージを表示します。
- @ オーバーライド
- public void onCreate ( Bundle savedInstanceState ) {
- スーパー 。 onCreate ( savedInstanceState ) ;
- TextView view = new TextView ( this ) ;
- 文字列メッセージ= getIntent ( ) 。 getStringExtra ( "message" ) ;
- if (メッセージ== null )
- ビュー。 setText ( "Hello、World !!!" ) ;
- 他に
- ビュー。 setText ( "Your message:" + message ) ;
- setContentView (ビュー) ;
- 文字列 ID = C2DMessaging。 getRegistrationId ( これ ) ;
- if ( id == "" )
- {
- C2DMessaging。 register ( this 、 "<yourmail> @ gmail.com" ) ;
- }
- }
実際には、登録IDをサーバーやその他の小さなものに転送するためのメカニズムを作成する必要がありますが、 この例では、そこで停止し、サーバー上の登録IDをハードコードします。
サーバー側
最初のステップは、認証トークンを取得することです。 これはこのブログによく書かれています(他にも多くの有用な情報があります)。 一般に、例があり、すべてがロシア語であるため、繰り返しは行わず、受け取ったと想定します。
したがって、登録IDと認証トークンがあります。 小さい場合は、メッセージを伝える必要があります。 何らかの理由で、インターネットのこの部分に関する情報はほとんどありませんが、ここでは複雑なことは何もありません。
Googleサーバーとの接続を確立し、 正しい形式でリクエストを作成する必要があります。 そしてそれだけです。 メッセージを送信する簡単な例を次のコードに示します。
- public boolean sendData (
- 文字列 authToken、
- 文字列 registrationId、
- 文字列の崩壊、
- 文字列キー、
- 文字列値)
- IOExceptionを スローします {
- //登録IDを設定します
- StringBuilder postDataBuilder = new StringBuilder ( ) ;
- postDataBuilder。 append ( "registration_id" ) 。
- append ( "=" ) 。 追加 ( registrationId ) ;
- // collapse_keyを設定-折りたたみキーが同じ場合にメッセージをグループ化し、
- //たとえば、デバイスはオフになり、1つのメッセージのみが送信されます。
- //一度にすべてではありません。
- postDataBuilder。 append ( "&" ) 。 append ( "collapse_key" ) 。 append ( "=" ) 。
- 追加 (折りたたみ) ;
- // <data。> <key> = <value>の形式で、送信された日付を追加します
- postDataBuilder。 append ( "&" ) 。 append ( "data。" + key ) 。 append ( "=" ) 。
- append ( URLEncoder。encode ( value、 "UTF-8" ) ) ;
- バイト [ ] postData = postDataBuilder。 toString ( ) 。 getBytes ( "UTF-8" ) ;
- URL url = 新しい URL ( " android.clients.google.com/c2dm/send" ) ;
- //接続を設定します
- //プロキシproxy = new Proxy(Proxy.Type.HTTP、new InetSocketAddress( "lazerboy.local"、8080));
- HttpsURLConnection conn = ( HttpsURLConnection ) url。 openConnection ( / *プロキシ* / ) ;
- conn。 setDoOutput ( true ) ;
- conn。 setHostnameVerifier ( this .new MyHostnameVerifier ( ) ) ;
- conn。 setRequestMethod ( "POST" ) ;
- conn。 setRequestProperty ( "Content-Type" 、
- "application / x-www-form-urlencoded; charset = UTF-8" ) ;
- conn。 setRequestProperty ( "Content-Length" 、Integer。toString ( postData。length ) ) ;
- conn。 setRequestProperty ( "Authorization" 、 "GoogleLogin auth =" + authToken ) ;
- OutputStream out = conn。 getOutputStream ( ) ;
- アウト。 書き込み ( postData ) ;
- アウト。 閉じる ( ) ;
- //応答コードを取得します。
- int responseCode = conn。 getResponseCode ( ) ;
- if ( responseCode == HttpServletResponse。SC_UNAUTHORIZED ||
- responseCode == HttpServletResponse。 SC_FORBIDDEN ) {
- システム アウト 。 printf ( 「無許可-トークンが必要」 ) ;
- falseを 返し ます 。
- }
- if ( responseCode == HttpServletResponse。SC_OK )
- {
- システム アウト 。 printf ( "デバイスに送信されたデータ!" ) ;
- trueを 返し ます 。
- }
- システム アウト 。 printf ( "何か間違っている、応答メッセージ:" 、接続getResponseMessage ( ) ) ;
- falseを 返し ます 。
- }
ここでは、サーバーからのメッセージの処理という非常に重要な詳細が1つ欠落しています。 そのため、たとえば、認証トークンの有効期限が切れる状況は一切処理されません。 しかし、すぐに使用できるライブラリではなく、動作するアプリケーションの例を書きたかったのです。 さらに、必要なものはすべて、Googleのソースコード(たとえば、 こちら)にあります 。
コードが正常に機能する場合、ユーザーのデバイスの通知領域に通知が表示され、クリックすると、サーバーからのテキストでアプリケーションが起動します。 ほら、これはまさに私たちが達成したことです。
おわりに
AndroidベースのアプリケーションにC2DMサポートをそれほど頭痛なく固定できる方法を示しました。 クライアント側とサーバー側の両方で、このために必要なこと。 もちろん、「ホワイトスポット」はありましたが、繰り返しますが、主なタスクは、この投稿を読んでいる人がC2DMをサポートするアプリケーションの開発を容易にするためのアイデアを伝え、コード例を示すことでした。