鲁能李微老婆:Android C2DM学习——云端推送

来源:百度文库 编辑:九乡新闻网 时间:2024/07/07 13:45:17
Android C2DM学习——云端推送分类: Google AndroidAndroid C2DM2011-07-07 18:221532人阅读评论(30)收藏举报

一.基础知识

当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如《地震及时通》就需要及时获取服务器上最新的地震信息。要获取服务器上不定时更新的信息一般来说有两种方法,第一种是客户端使用Pull(拉)的方式,隔一段时间就去服务器上获取信息,看是否有更新的信息出现。第二种就是服务器使用Push(推送)的方式,当服务器端有新信息了,则把最新的信息Push到客户端上。

虽然Pull和Push两种方式都能实现获取服务器端更新信息的功能,但是明显来说Push is better than pull。因为Pull方式更费客户端的网络流量,更主要的是费电量。

Android从2.2版本开始增加了Cloud to Device Messaging(C2DM)框架,在系统中支持了Push功能,基于Android平台使用Push功能更加简单了。虽然C2DM目前还处在实验室阶段,不过小规模的使用应该没问题

下面我们就来体验一下Android的C2DM功能。

二.C2DM框架

使用Android的C2DM功能有几个要求:

1. 需要Android2.2及以上的系统版本。

2. 使用C2DM功能的Android设备上需要设置好Google的账户。

3. 需要在这里注册使用C2DM功能的用户邮箱账号(最好为C2DM单独注册一个Gmail邮箱账号)。

我们接下来C2DM的一个完整过程,这里借用一下Google官方推出的Chrome To Phone过程图来说明下。

图1 C2DM操作过程图

要使用C2DM来进行Push操作,基本上要使用以下6个步骤:

(1)注册:Android设备把使用C2DM功能的用户账户(比如android.c2dm.demo@gmail.com)和App名称发送给C2DM服务器。

(2)C2DM服务器会返回一个registration_id值给Android设备,设备需要保存这个registration_id值。

(3)Android设备把获得的registration_id和C2DM功能的用户账户(android.c2dm.demo@gmail.com)发送给自己的服务器,不过一般用户账户信息因为和服务器确定好的,所以不必发送。

这样Android设备就完成了C2DM功能的注册过程,接下来就可以接收C2DM服务器Push过来的消息了。

(4)服务器获得数据。这里图中的例子Chrome To Phone,服务器接收到Chrome浏览器发送的数据。数据也可以是服务器本地产生的。这里的服务器是Google AppEngine(很好的一项服务,可惜在国内被屏了),要换成自己的服务器。服务器还要获取注册使用C2DM功能的用户账户(android.c2dm.demo@gmail.com)的ClientLogin权限Auth。

(5)服务器把要发送的数据和registration_id一起,并且头部带上获取的Auth,使用POST的方式发送给C2DM服务器。

(6)C2DM服务器会以Push的方式把数据发送给对应的Android设备,Android设备只要在程序中按之前和服务器商量好的格式从对应的key中获取数据即可。

这样我们就大概明白了C2DM的工作流程,下面我们就结合一个实例来具体的说明以上6个步骤。

三.实例开发

我们要创建的程序名称为AndroidC2DMDemo,包名为com.ichliebephone.c2dm。

开始之前我们先去C2DM网页上注册一下使用C2DM功能的用户账户。

图2 应用程序名

其中应用程序名要填写带包名的完整名称,比如这里为om.ichliebephone.c2dm. AndroidC2DMDemo。

图3 C2DM用户账户注册

这里的contact邮箱使用一个你能接收到邮件的邮箱即可,下面的Role(sender)account邮箱最好单独注册一个Gmail邮箱来使用C2DM服务。我们这里使用的是专门注册的android.c2dm.deno@gmail.com邮箱。

提交后,过一段时间就会收到Google发送过来的确认邮件,然后你就可以使用C2DM的Push服务了。

介绍了这么多,我们先来快速完成一个实例,只完成Android设备端的注册部分,不包含向服务器发送registration_id和服务器向C2DM服务器发送数据的具体代码,这部分只是用Ubuntu下的curl命令来模拟,主要是快速亲自体验一下Push的结果。

创建一个Android工程AndroidC2DMDemo,并且包含进Google的开源例子Chrome To Phone中的c2dm包com.google.android.c2dm,包中包含三个Java类,分别为:

第一个类为C2DMBaseReceiver:

view plaincopy to clipboardprint?
  1. /*
  2. * Copyright 2010 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.google.android.c2dm;
  17. import java.io.IOException;
  18. import android.app.AlarmManager;
  19. import android.app.IntentService;
  20. import android.app.PendingIntent;
  21. import android.content.Context;
  22. import android.content.Intent;
  23. import android.os.PowerManager;
  24. import android.util.Log;
  25. /**
  26. * Base class for C2D message receiver. Includes constants for the
  27. * strings used in the protocol.
  28. */
  29. /**
  30. * 接收和处理C2DM消息的基类
  31. * */
  32. public abstract class C2DMBaseReceiver extends IntentService {
  33. //和C2DM Push的Intent内容相关
  34. //重新向C2DM服务器注册
  35. private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";
  36. //向C2DM服务器注册后的回调处理
  37. public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";
  38. //接收到C2DM服务器的推送消息
  39. private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";
  40. // Logging tag
  41. private static final String TAG = "C2DM";
  42. // Extras in the registration callback intents.
  43. //向C2DM注册返回的intent中包含的key
  44. public static final String EXTRA_UNREGISTERED = "unregistered";
  45. public static final String EXTRA_ERROR = "error";
  46. public static final String EXTRA_REGISTRATION_ID = "registration_id";
  47. //向C2DM注册出错的原因
  48. public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
  49. public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
  50. public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
  51. public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
  52. public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
  53. public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
  54. public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
  55. // wakelock
  56. private static final String WAKELOCK_KEY = "C2DM_LIB";
  57. private static PowerManager.WakeLock mWakeLock;
  58. private final String senderId;
  59. /**
  60. * The C2DMReceiver class must create a no-arg constructor and pass the
  61. * sender id to be used for registration.
  62. */
  63. public C2DMBaseReceiver(String senderId) {
  64. // senderId is used as base name for threads, etc.
  65. super(senderId);
  66. this.senderId = senderId;
  67. }
  68. //下面几个是接收到C2DM Push过来的信息后的回调函数,都可以在继承的子类中处理
  69. /**
  70. * Called when a cloud message has been received.
  71. */
  72. /**
  73. * 接收到C2DM服务器Push的消息后的回调函数,需要在继承的子类中处理
  74. * */
  75. protected abstract void onMessage(Context context, Intent intent);
  76. /**
  77. * Called on registration error. Override to provide better
  78. * error messages.
  79. *
  80. * This is called in the context of a Service - no dialog or UI.
  81. */
  82. /**
  83. * 出错的回调函数
  84. * */
  85. public abstract void onError(Context context, String errorId);
  86. /**
  87. * Called when a registration token has been received.
  88. */
  89. /**
  90. * 注册后的回调函数
  91. * */
  92. public void onRegistered(Context context, String registrationId) throws IOException {
  93. // registrationId will also be saved
  94. }
  95. /**
  96. * Called when the device has been unregistered.
  97. */
  98. /**
  99. * 取消注册的回调函数
  100. * */
  101. public void onUnregistered(Context context) {
  102. }
  103. //IntentService的方法
  104. @Override
  105. public final void onHandleIntent(Intent intent) {
  106. try {
  107. Context context = getApplicationContext();
  108. if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
  109. handleRegistration(context, intent);//处理注册后的回调
  110. } else if (intent.getAction().equals(C2DM_INTENT)) {
  111. onMessage(context, intent);//处理C2DM Push消息的回调
  112. } else if (intent.getAction().equals(C2DM_RETRY)) {
  113. C2DMessaging.register(context, senderId); //重新注册
  114. }
  115. } finally {
  116. // Release the power lock, so phone can get back to sleep.
  117. // The lock is reference counted by default, so multiple
  118. // messages are ok.
  119. // If the onMessage() needs to spawn a thread or do something else,
  120. // it should use it's own lock.
  121. mWakeLock.release();
  122. }
  123. }
  124. /**
  125. * Called from the broadcast receiver.
  126. * Will process the received intent, call handleMessage(), registered(), etc.
  127. * in background threads, with a wake lock, while keeping the service
  128. * alive.
  129. */
  130. static void runIntentInService(Context context, Intent intent) {
  131. if (mWakeLock == null) {
  132. // This is called from BroadcastReceiver, there is no init.
  133. PowerManager pm =
  134. (PowerManager) context.getSystemService(Context.POWER_SERVICE);
  135. mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
  136. WAKELOCK_KEY);
  137. }
  138. mWakeLock.acquire();
  139. // Use a naming convention, similar with how permissions and intents are
  140. // used. Alternatives are introspection or an ugly use of statics.
  141. String receiver = context.getPackageName() + ".C2DMReceiver";
  142. intent.setClassName(context, receiver);
  143. context.startService(intent);
  144. }
  145. //处理注册后的回调
  146. private void handleRegistration(final Context context, Intent intent) {
  147. final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
  148. String error = intent.getStringExtra(EXTRA_ERROR);
  149. String removed = intent.getStringExtra(EXTRA_UNREGISTERED);
  150. Log.v(TAG, "handleRegistration");
  151. //打印出接收到的registraton_id
  152. Log.v(TAG, "dmControl: registrationId = " + registrationId +
  153. ", error = " + error + ", removed = " + removed);
  154. if (Log.isLoggable(TAG, Log.DEBUG)) {
  155. Log.d(TAG, "dmControl: registrationId = " + registrationId +
  156. ", error = " + error + ", removed = " + removed);
  157. }
  158. if (removed != null) {
  159. // Remember we are unregistered
  160. C2DMessaging.clearRegistrationId(context);
  161. onUnregistered(context);
  162. return;
  163. } else if (error != null) {
  164. // we are not registered, can try again
  165. C2DMessaging.clearRegistrationId(context);
  166. // Registration failed
  167. Log.e(TAG, "Registration error " + error);
  168. onError(context, error);
  169. if ("SERVICE_NOT_AVAILABLE".equals(error)) {
  170. long backoffTimeMs = C2DMessaging.getBackoff(context);
  171. Log.d(TAG, "Scheduling registration retry, backoff = " + backoffTimeMs);
  172. Intent retryIntent = new Intent(C2DM_RETRY);
  173. PendingIntent retryPIntent = PendingIntent.getBroadcast(context,
  174. 0 /*requestCode*/, retryIntent, 0 /*flags*/);
  175. AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
  176. am.set(AlarmManager.ELAPSED_REALTIME,
  177. backoffTimeMs, retryPIntent);
  178. // Next retry should wait longer.
  179. backoffTimeMs *= 2;
  180. C2DMessaging.setBackoff(context, backoffTimeMs);
  181. }
  182. } else {
  183. try {
  184. onRegistered(context, registrationId);
  185. C2DMessaging.setRegistrationId(context, registrationId);
  186. } catch (IOException ex) {
  187. Log.e(TAG, "Registration error " + ex.getMessage());
  188. }
  189. }
  190. }
  191. }


第二个类为C2DMBroadcastReceiver:

view plaincopy to clipboardprint?
  1. /*
  2. */
  3. package com.google.android.c2dm;
  4. import android.app.Activity;
  5. import android.content.BroadcastReceiver;
  6. import android.content.Context;
  7. import android.content.Intent;
  8. /**
  9. * Helper class to handle BroadcastReciver behavior.
  10. * - can only run for a limited amount of time - it must start a real service
  11. * for longer activity
  12. * - must get the power lock, must make sure it's released when all done.
  13. *
  14. */
  15. /**
  16. * 帮助类,帮忙处理BroadcastReciver过程
  17. * */
  18. public class C2DMBroadcastReceiver extends BroadcastReceiver {
  19. @Override
  20. public final void onReceive(Context context, Intent intent) {
  21. // To keep things in one place.
  22. C2DMBaseReceiver.runIntentInService(context, intent);
  23. setResult(Activity.RESULT_OK, null /* data */, null /* extra */);
  24. }
  25. }


第三个类为C2DMessaging:

view plaincopy to clipboardprint?
  1. /*
  2. * Copyright 2010 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.google.android.c2dm;
  17. import android.app.PendingIntent;
  18. import android.content.Context;
  19. import android.content.Intent;
  20. import android.content.SharedPreferences;
  21. import android.content.SharedPreferences.Editor;
  22. /**
  23. * Utilities for device registration.
  24. *
  25. * Will keep track of the registration token in a private preference.
  26. */
  27. /**
  28. * 和注册相关的一些实用函数
  29. * */
  30. public class C2DMessaging {
  31. public static final String EXTRA_SENDER = "sender";
  32. public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
  33. public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
  34. public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
  35. public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
  36. public static final String BACKOFF = "backoff";
  37. public static final String GSF_PACKAGE = "com.google.android.gsf"; //GSF为GoogleServicesFramework.apk的缩写
  38. // package
  39. static final String PREFERENCE = "com.google.android.c2dm";
  40. private static final long DEFAULT_BACKOFF = 30000;
  41. /**
  42. * Initiate c2d messaging registration for the current application
  43. */
  44. public static void register(Context context,
  45. String senderId) {
  46. Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
  47. registrationIntent.setPackage(GSF_PACKAGE);
  48. registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
  49. PendingIntent.getBroadcast(context, 0, new Intent(), 0));
  50. registrationIntent.putExtra(EXTRA_SENDER, senderId);
  51. context.startService(registrationIntent);
  52. // TODO: if intent not found, notification on need to have GSF
  53. }
  54. /**
  55. * Unregister the application. New messages will be blocked by server.
  56. */
  57. public static void unregister(Context context) {
  58. Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
  59. regIntent.setPackage(GSF_PACKAGE);
  60. regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context,
  61. 0, new Intent(), 0));
  62. context.startService(regIntent);
  63. }
  64. /**
  65. * Return the current registration id.
  66. *
  67. * If result is empty, the registration has failed.
  68. *
  69. * @return registration id, or empty string if the registration is not complete.
  70. */
  71. public static String getRegistrationId(Context context) {
  72. final SharedPreferences prefs = context.getSharedPreferences(
  73. PREFERENCE,
  74. Context.MODE_PRIVATE);
  75. String registrationId = prefs.getString("dm_registration", "");
  76. return registrationId;
  77. }
  78. public static long getLastRegistrationChange(Context context) {
  79. final SharedPreferences prefs = context.getSharedPreferences(
  80. PREFERENCE,
  81. Context.MODE_PRIVATE);
  82. return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
  83. }
  84. static long getBackoff(Context context) {
  85. final SharedPreferences prefs = context.getSharedPreferences(
  86. PREFERENCE,
  87. Context.MODE_PRIVATE);
  88. return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
  89. }
  90. static void setBackoff(Context context, long backoff) {
  91. final SharedPreferences prefs = context.getSharedPreferences(
  92. PREFERENCE,
  93. Context.MODE_PRIVATE);
  94. Editor editor = prefs.edit();
  95. editor.putLong(BACKOFF, backoff);
  96. editor.commit();
  97. }
  98. // package
  99. static void clearRegistrationId(Context context) {
  100. final SharedPreferences prefs = context.getSharedPreferences(
  101. PREFERENCE,
  102. Context.MODE_PRIVATE);
  103. Editor editor = prefs.edit();
  104. editor.putString("dm_registration", "");
  105. editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
  106. editor.commit();
  107. }
  108. // package
  109. static void setRegistrationId(Context context, String registrationId) {
  110. final SharedPreferences prefs = context.getSharedPreferences(
  111. PREFERENCE,
  112. Context.MODE_PRIVATE);
  113. Editor editor = prefs.edit();
  114. editor.putString("dm_registration", registrationId);
  115. editor.commit();
  116. }
  117. }


代码中已添加了部分中文注释,可以先大概了解下,等整个工程建立完了在一起解释。

然后创建我们自己的包com.ichliebephone.c2dm,包含两个类,一个是工程的入口AndroidC2DMDemo:

view plaincopy to clipboardprint?
  1. package com.ichliebephone.c2dm;
  2. import com.google.android.c2dm.C2DMessaging;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.util.Log;
  6. public class AndroidC2DMDemo extends Activity {
  7. /** Called when the activity is first created. */
  8. private static final String TAG = "AndroidC2DMDemo";
  9. public static final String SENDER_ID = "android.c2dm.demo@gmail.com"; //使用C2DM服务的用户账户
  10. public static final String MESSAGE_KEY_ONE = "msg"; //和服务器商量好的接收消息的键值key
  11. @Override
  12. public void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.main);
  15. Log.v(TAG, "Start");
  16. //向C2DM服务器注册
  17. C2DMessaging.register(this, SENDER_ID);
  18. }
  19. }


很简单,就是开始向C2DM服务器进行注册。

另一个类为C2DMBaseReceiver的子类C2DMReceiver:

view plaincopy to clipboardprint?
  1. package com.ichliebephone.c2dm;
  2. import java.io.IOException;
  3. import android.app.Notification;
  4. import android.app.NotificationManager;
  5. import android.app.PendingIntent;
  6. import android.content.Context;
  7. import android.content.Intent;
  8. import android.os.Bundle;
  9. import android.util.Log;
  10. import com.google.android.c2dm.C2DMBaseReceiver;
  11. //接收C2DM服务器Push的消息,包括注册返回的registration_id消息,推送的数据消息等
  12. public class C2DMReceiver extends C2DMBaseReceiver{
  13. private static final String TAG="C2DMReceiver";
  14. //
  15. public C2DMReceiver()
  16. {
  17. super(AndroidC2DMDemo.SENDER_ID);
  18. }
  19. public C2DMReceiver(String senderId) {
  20. super(senderId);
  21. // TODO Auto-generated constructor stub
  22. }
  23. //接收到Push消息的回调函数
  24. @Override
  25. protected void onMessage(Context context, Intent intent) {
  26. // TODO Auto-generated method stub
  27. Log.v(TAG, "C2DMReceiver message");
  28. Bundle extras = intent.getExtras();
  29. if(extras!=null){
  30. String msg = (String)extras.get(AndroidC2DMDemo.MESSAGE_KEY_ONE);
  31. Log.v(TAG, "The received msg = "+msg);
  32. //在标题栏上显示通知
  33. NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
  34. Notification notification = new Notification(R.drawable.icon, msg, System.currentTimeMillis());
  35. PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AndroidC2DMDemo.class), 0);
  36. notification.setLatestEventInfo(this, getString(R.string.app_name), msg, contentIntent);
  37. notificationManager.notify(0, notification);
  38. }
  39. }
  40. @Override
  41. public void onError(Context context, String errorId) {
  42. // TODO Auto-generated method stub
  43. Log.v(TAG, "C2DMReceiver error");
  44. }
  45. @Override
  46. public void onRegistered(Context context, String registrationId)
  47. throws IOException {
  48. // TODO Auto-generated method stub
  49. super.onRegistered(context, registrationId);
  50. Log.v(TAG, "C2DMReceiver Register");
  51. }
  52. @Override
  53. public void onUnregistered(Context context) {
  54. // TODO Auto-generated method stub
  55. super.onUnregistered(context);
  56. Log.v(TAG, "C2DMReceiver UnRegister");
  57. }
  58. }


在这个类中我们主要在接收到Push的回调函数onMessage中对消息进行了接收,并且使用Notification的方式显示在状态栏上。

我们完整的工程目录是这样的:

图4 工程目录

最后我们还要在AndroidManifest.xml中增加对应的权限等内容:

view plaincopy to clipboardprint?
  1. version="1.0" encoding="utf-8"?>
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.ichliebephone.c2dm"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. android:minSdkVersion="8" />
  7. android:name="com.ichliebephone.c2dm.permission.C2D_MESSAGE"
  8. android:protectionLevel="signature">
  9. android:name="com.ichliebephone.c2dm.permission.C2D_MESSAGE"/>
  10. android:name="com.google.android.c2dm.permission.RECEIVE" />
  11. android:name="android.permission.INTERNET" />
  12. android:name="android.permission.WAKE_LOCK" />
  13. android:name="android.permission.GET_ACCOUNTS" />
  14. android:name="android.permission.USE_CREDENTIALS" />
  15. android:icon="@drawable/icon" android:label="@string/app_name">
  16. android:name=".AndroidC2DMDemo"
  17. android:label="@string/app_name">
  18. android:name="android.intent.action.MAIN" />
  19. android:name="android.intent.category.LAUNCHER" />
  20. android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
  21. android:permission="com.google.android.c2dm.permission.SEND">
  22. android:name="com.google.android.c2dm.intent.RECEIVE" />
  23. android:name="com.ichliebephone.c2dm" />
  24. android:name="com.google.android.c2dm.intent.REGISTRATION" />
  25. android:name="com.ichliebephone.c2dm" />


因为C2DM功能只有2.2及以上的Android系统才支持,因此创建一个2.2及以上的AVD,然后在”设置->账户与同步”里还要设置好Google Account,如下图所示:

图5 设置Android设备中的Google账户

然后就可以运行程序了,我们会在DDMS输出中看到获得的registration_id:

图6 获得的registration_id

如果第一次运行没有出现,试着再运行一次。

有了registration_id,我们的服务器端就可以向C2DM端发送需要Push的数据了,这里进行简单化处理下,在Ubuntu下直接使用curl命令来模拟服务器功能向C2DM发送数据。

我们先来获取C2DM的ClientLogin权限Auth,在Ubuntu终端下输入:

view plaincopy to clipboardprint?
  1. lingaohe@lingaohe-laptop:~$ curl -d "accountType=HOSTED_OR_GOOGLE&Email=android.c2dm.demo@gmail.com&Passwd=androidc2dmdemo&service=ac2dm&source=bupt-c2dmdemo-1.0" https://www.google.com/accounts/ClientLogin


这个表示以POST的方式向https://www.google.com/accounts/ClientLogin发送数据,其中把Email和Passwd换成你自己在C2DM网页上注册的邮箱号和密码。

如果你的邮箱已在C2DM网页上注册,并且密码没有错误的话就会返回需要的Auth内容:

view plaincopy to clipboardprint?
  1. SID=DQAAAKYAAADcTtHbBBNcZJEOfkfVRycD_ZOIidwsQ3UwIY7cSrYWaY6uhlfo0l9gRPB-mQxP4K2T5tWiG--vWVmSTeq5p8SPwgnsYvfzj7bkNiPPIy4xRimVVfBmAHnZgLohw7gHMKi5DS6kK-Ut5tNzdTkI0I2tUDF0ryQ7MnPpI6Sj-gUCyBXmvKatHHDnNTTV78XdGIx7FYej1DyqGsPsYo3bCstHgltjv3cd2Hs7D4yrpUWHZw
  2. LSID=DQAAAKgAAABCpaoUE4XvxM24Cofntw1IUGx5fKxX-m7aqTL0zhunP0OjzJ2sn9ywmPa1BMZ2cF2IchuxHFLVzaSQfydAmiHZJGXLgaUorpIN6yz1e0VFWKmS6j4wGjZOos3QoJ9rkha0jKbOiHfBesADjxk-qjJ24TJ0RL-xkZHQyzS69YlA1KyzqIKjAMCzgqaDfCwhqxylJzizJksO2h8xpAFXZ38d_grm8XYZtzejiCiAMAR65A
  3. Auth=DQAAAKoAAACRF4pgYULnXULoWgbwfdqmMiRhfZYa1l-LW_rwGD7cofov4L4c2bVrtCOXbEbkju_hhqdAonpMkrb5icptt28fU8c-s-u1y2MXNYDxPIdQzfA2t6oI3NTmyj35MpsR1NKL4TN7ZVEn6z9NueuiKAqLHukZYh1YMGkGC8M6rVvA7AWPW36064XCQED7KLVNp_pGT00lrni7UdZKZWEy0FT-EVR-OxDyHWw6C-5Kmfkisw


返回的内容包括SID,LSID和Auth三个部分,其中Auth是我们需要的内容。

有了Auth和registration_id值后,我们就可以继续用curl命令模拟我们自己服务器的功能向C2DM发送要推送的数据:

view plaincopy to clipboardprint?
  1. lingaohe@lingaohe-laptop:~$ curl -H "Authorization:GoogleLogin auth=DQAAAKoAAACRF4pgYULnXULoWgbwfdqmMiRhfZYa1l-LW_rwGD7cofov4L4c2bVrtCOXbEbkju_hhqdAonpMkrb5icptt28fU8c-s-u1y2MXNYDxPIdQzfA2t6oI3NTmyj35MpsR1NKL4TN7ZVEn6z9NueuiKAqLHukZYh1YMGkGC8M6rVvA7AWPW36064XCQED7KLVNp_pGT00lrni7UdZKZWEy0FT-EVR-OxDyHWw6C-5Kmfkisw" -d "registration_id=APA91bGUBoSvt3G5Ny9t0IGLmIKAKYX6G6VHwSQHh3tP2fqcaQ0N4GPdKh5B3RDUHFCFF06YwT8ifOP_cOy5BAWyCLHL8d8NpuIW9AqXt9h2JSBVF2MitZA&collapse_key=1&data.msg=ichliebejiajia" https://android.apis.google.com/c2dm/send


其中发送的数据部分为data.msg=ichliebejiajia,表示发送的数据内容为ichliebejiajia,键值为msg,键值得和Android终端上的程序统一好,以便终端上可以获取。如果发送成功,会返回一个id值,比如:

view plaincopy to clipboardprint?
  1. id=0:1308623423080544%6c5c15c200000031
  2. lingaohe@lingaohe-laptop:~$


这时我们的服务器就已经把数据发送给C2DM服务器了,Android设备上一会就能接收到C2DM服务器Push的数据。

在我们的例子中我们可以看到DDMS中打印出的消息:

图7 获取到的Push数据

同时Android模拟器的状态栏上会有对应的通知显示:

图8 Android模拟器接收到的Push数据

这样我们就快速实现了下Android的C2DM框架的Push功能。进一步的具体解释说明及服务器端的代码处理我们以后再学习。

文章对应的完整代码例子下载地址:

http://download.csdn.net/source/3425855