一.基础知识
当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如《地震及时通》就需要及时获取服务器上最新的地震信息。要获取服务器上不定时更新的信息一般来说有两种方法,第一种是客户端使用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功能。
使用Android的C2DM功能有几个要求:
1. 需要Android2.2及以上的系统版本。
2. 使用C2DM功能的Android设备上需要设置好Google的账户。
3. 需要在这里注册使用C2DM功能的用户邮箱账号(最好为C2DM单独注册一个Gmail邮箱账号)。
我们接下来C2DM的一个完整过程,这里借用一下Google官方推出的Chrome To Phone过程图来说明下。
要使用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:
C2DMBaseReceiver 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 17 package com.google.android.c2dm; 18 19 import java.io.IOException; 20 21 import android.app.AlarmManager; 22 import android.app.IntentService; 23 import android.app.PendingIntent; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.PowerManager; 27 import android.util.Log; 28 29 /** 30 * Base class for C2D message receiver. Includes constants for the 31 * strings used in the protocol. 32 */ 33 /** 34 * 接收和处理C2DM消息的基类 35 * */ 36 public abstract class C2DMBaseReceiver extends IntentService { 37 //和C2DM Push的Intent内容相关 38 //重新向C2DM服务器注册 39 private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY"; 40 //向C2DM服务器注册后的回调处理 41 public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION"; 42 //接收到C2DM服务器的推送消息 43 private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE"; 44 45 // Logging tag 46 private static final String TAG = "C2DM"; 47 48 // Extras in the registration callback intents. 49 //向C2DM注册返回的intent中包含的key 50 public static final String EXTRA_UNREGISTERED = "unregistered"; 51 public static final String EXTRA_ERROR = "error"; 52 public static final String EXTRA_REGISTRATION_ID = "registration_id"; 53 //向C2DM注册出错的原因 54 public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE"; 55 public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING"; 56 public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED"; 57 public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS"; 58 public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS"; 59 public static final String ERR_INVALID_SENDER = "INVALID_SENDER"; 60 public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR"; 61 62 // wakelock 63 private static final String WAKELOCK_KEY = "C2DM_LIB"; 64 65 private static PowerManager.WakeLock mWakeLock; 66 private final String senderId; 67 68 /** 69 * The C2DMReceiver class must create a no-arg constructor and pass the 70 * sender id to be used for registration. 71 */ 72 public C2DMBaseReceiver(String senderId) { 73 // senderId is used as base name for threads, etc. 74 super(senderId); 75 this.senderId = senderId; 76 } 77 //下面几个是接收到C2DM Push过来的信息后的回调函数,都可以在继承的子类中处理 78 /** 79 * Called when a cloud message has been received. 80 */ 81 /** 82 * 接收到C2DM服务器Push的消息后的回调函数,需要在继承的子类中处理 83 * */ 84 protected abstract void onMessage(Context context, Intent intent); 85 86 /** 87 * Called on registration error. Override to provide better 88 * error messages. 89 * 90 * This is called in the context of a Service - no dialog or UI. 91 */ 92 /** 93 * 出错的回调函数 94 * */ 95 public abstract void onError(Context context, String errorId); 96 97 /** 98 * Called when a registration token has been received. 99 */100 /**101 * 注册后的回调函数102 * */103 public void onRegistered(Context context, String registrationId) throws IOException {104 // registrationId will also be saved105 }106 107 /**108 * Called when the device has been unregistered.109 */110 /**111 * 取消注册的回调函数112 * */113 public void onUnregistered(Context context) {114 }115 116 //IntentService的方法117 @Override118 public final void onHandleIntent(Intent intent) {119 try {120 Context context = getApplicationContext();121 if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {122 handleRegistration(context, intent);//处理注册后的回调123 } else if (intent.getAction().equals(C2DM_INTENT)) {124 onMessage(context, intent);//处理C2DM Push消息的回调125 } else if (intent.getAction().equals(C2DM_RETRY)) {126 C2DMessaging.register(context, senderId); //重新注册127 }128 } finally {129 // Release the power lock, so phone can get back to sleep.130 // The lock is reference counted by default, so multiple 131 // messages are ok.132 133 // If the onMessage() needs to spawn a thread or do something else,134 // it should use it's own lock.135 mWakeLock.release();136 }137 }138 139 140 /**141 * Called from the broadcast receiver. 142 * Will process the received intent, call handleMessage(), registered(), etc.143 * in background threads, with a wake lock, while keeping the service 144 * alive. 145 */146 static void runIntentInService(Context context, Intent intent) {147 if (mWakeLock == null) {148 // This is called from BroadcastReceiver, there is no init.149 PowerManager pm = 150 (PowerManager) context.getSystemService(Context.POWER_SERVICE);151 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 152 WAKELOCK_KEY);153 }154 mWakeLock.acquire();155 156 // Use a naming convention, similar with how permissions and intents are 157 // used. Alternatives are introspection or an ugly use of statics. 158 String receiver = context.getPackageName() + ".C2DMReceiver";159 intent.setClassName(context, receiver);160 161 context.startService(intent);162 163 }164 165 //处理注册后的回调166 private void handleRegistration(final Context context, Intent intent) {167 final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);168 String error = intent.getStringExtra(EXTRA_ERROR);169 String removed = intent.getStringExtra(EXTRA_UNREGISTERED);170 Log.v(TAG, "handleRegistration");171 //打印出接收到的registraton_id172 Log.v(TAG, "dmControl: registrationId = " + registrationId +173 ", error = " + error + ", removed = " + removed);174 if (Log.isLoggable(TAG, Log.DEBUG)) {175 Log.d(TAG, "dmControl: registrationId = " + registrationId +176 ", error = " + error + ", removed = " + removed);177 }178 if (removed != null) {179 // Remember we are unregistered180 C2DMessaging.clearRegistrationId(context);181 onUnregistered(context);182 return;183 } else if (error != null) {184 // we are not registered, can try again185 C2DMessaging.clearRegistrationId(context);186 // Registration failed187 Log.e(TAG, "Registration error " + error);188 onError(context, error);189 if ("SERVICE_NOT_AVAILABLE".equals(error)) {190 long backoffTimeMs = C2DMessaging.getBackoff(context);191 192 Log.d(TAG, "Scheduling registration retry, backoff = " + backoffTimeMs);193 Intent retryIntent = new Intent(C2DM_RETRY);194 PendingIntent retryPIntent = PendingIntent.getBroadcast(context, 195 0 /*requestCode*/, retryIntent, 0 /*flags*/);196 197 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);198 am.set(AlarmManager.ELAPSED_REALTIME,199 backoffTimeMs, retryPIntent);200 201 // Next retry should wait longer.202 backoffTimeMs *= 2;203 C2DMessaging.setBackoff(context, backoffTimeMs);204 } 205 } else {206 try {207 onRegistered(context, registrationId);208 C2DMessaging.setRegistrationId(context, registrationId);209 } catch (IOException ex) {210 Log.e(TAG, "Registration error " + ex.getMessage());211 }212 }213 }214 }第二个类为C2DMBroadcastReceiver:
C2DMBroadcastReceiver 1 /* 2 */ 3 package com.google.android.c2dm; 4 5 import android.app.Activity; 6 import android.content.BroadcastReceiver; 7 import android.content.Context; 8 import android.content.Intent; 9 10 /**11 * Helper class to handle BroadcastReciver behavior.12 * - can only run for a limited amount of time - it must start a real service 13 * for longer activity14 * - must get the power lock, must make sure it's released when all done.15 * 16 */17 /**18 * 帮助类,帮忙处理BroadcastReciver过程19 * */20 public class C2DMBroadcastReceiver extends BroadcastReceiver {21 22 @Override23 public final void onReceive(Context context, Intent intent) {24 // To keep things in one place.25 C2DMBaseReceiver.runIntentInService(context, intent);26 setResult(Activity.RESULT_OK, null /* data */, null /* extra */); 27 }28 }第三个类为C2DMessaging:
C2DMessaging 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 17 package com.google.android.c2dm; 18 19 import android.app.PendingIntent; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.SharedPreferences; 23 import android.content.SharedPreferences.Editor; 24 25 /** 26 * Utilities for device registration. 27 * 28 * Will keep track of the registration token in a private preference. 29 */ 30 /** 31 * 和注册相关的一些实用函数 32 * */ 33 public class C2DMessaging { 34 public static final String EXTRA_SENDER = "sender"; 35 public static final String EXTRA_APPLICATION_PENDING_INTENT = "app"; 36 public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER"; 37 public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER"; 38 public static final String LAST_REGISTRATION_CHANGE = "last_registration_change"; 39 public static final String BACKOFF = "backoff"; 40 public static final String GSF_PACKAGE = "com.google.android.gsf"; //GSF为GoogleServicesFramework.apk的缩写 41 42 43 // package 44 static final String PREFERENCE = "com.google.android.c2dm"; 45 46 private static final long DEFAULT_BACKOFF = 30000; 47 48 /** 49 * Initiate c2d messaging registration for the current application 50 */ 51 public static void register(Context context, 52 String senderId) { 53 Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT); 54 registrationIntent.setPackage(GSF_PACKAGE); 55 registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, 56 PendingIntent.getBroadcast(context, 0, new Intent(), 0)); 57 registrationIntent.putExtra(EXTRA_SENDER, senderId); 58 context.startService(registrationIntent); 59 // TODO: if intent not found, notification on need to have GSF 60 } 61 62 /** 63 * Unregister the application. New messages will be blocked by server. 64 */ 65 public static void unregister(Context context) { 66 Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT); 67 regIntent.setPackage(GSF_PACKAGE); 68 regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context, 69 0, new Intent(), 0)); 70 context.startService(regIntent); 71 } 72 73 /** 74 * Return the current registration id. 75 * 76 * If result is empty, the registration has failed. 77 * 78 * @return registration id, or empty string if the registration is not complete. 79 */ 80 public static String getRegistrationId(Context context) { 81 final SharedPreferences prefs = context.getSharedPreferences( 82 PREFERENCE, 83 Context.MODE_PRIVATE); 84 String registrationId = prefs.getString("dm_registration", ""); 85 return registrationId; 86 } 87 88 public static long getLastRegistrationChange(Context context) { 89 final SharedPreferences prefs = context.getSharedPreferences( 90 PREFERENCE, 91 Context.MODE_PRIVATE); 92 return prefs.getLong(LAST_REGISTRATION_CHANGE, 0); 93 } 94 95 static long getBackoff(Context context) { 96 final SharedPreferences prefs = context.getSharedPreferences( 97 PREFERENCE, 98 Context.MODE_PRIVATE); 99 return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);100 }101 102 static void setBackoff(Context context, long backoff) {103 final SharedPreferences prefs = context.getSharedPreferences(104 PREFERENCE,105 Context.MODE_PRIVATE);106 Editor editor = prefs.edit();107 editor.putLong(BACKOFF, backoff);108 editor.commit();109 110 }111 112 // package113 static void clearRegistrationId(Context context) {114 final SharedPreferences prefs = context.getSharedPreferences(115 PREFERENCE,116 Context.MODE_PRIVATE);117 Editor editor = prefs.edit();118 editor.putString("dm_registration", "");119 editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());120 editor.commit();121 122 }123 124 // package125 static void setRegistrationId(Context context, String registrationId) {126 final SharedPreferences prefs = context.getSharedPreferences(127 PREFERENCE,128 Context.MODE_PRIVATE);129 Editor editor = prefs.edit();130 editor.putString("dm_registration", registrationId);131 editor.commit();132 133 }134 }代码中已添加了部分中文注释,可以先大概了解下,等整个工程建立完了在一起解释。
然后创建我们自己的包com.ichliebephone.c2dm,包含两个类,一个是工程的入口AndroidC2DMDemo:
AndroidC2DMDemo 1 package com.ichliebephone.c2dm; 2 3 import com.google.android.c2dm.C2DMessaging; 4 5 import android.app.Activity; 6 import android.os.Bundle; 7 import android.util.Log; 8 9 public class AndroidC2DMDemo extends Activity {10 /** Called when the activity is first created. */11 private static final String TAG = "AndroidC2DMDemo";12 public static final String SENDER_ID = "android.c2dm.demo@gmail.com"; //使用C2DM服务的用户账户13 public static final String MESSAGE_KEY_ONE = "msg"; //和服务器商量好的接收消息的键值key14 15 @Override16 public void onCreate(Bundle savedInstanceState) {17 super.onCreate(savedInstanceState);18 setContentView(R.layout.main);19 20 Log.v(TAG, "Start");21 //向C2DM服务器注册22 C2DMessaging.register(this, SENDER_ID);23 }24 25 26 }很简单,就是开始向C2DM服务器进行注册。
另一个类为C2DMBaseReceiver的子类C2DMReceiver:
C2DMReceiver 1 package com.ichliebephone.c2dm; 2 3 import java.io.IOException; 4 5 import android.app.Notification; 6 import android.app.NotificationManager; 7 import android.app.PendingIntent; 8 import android.content.Context; 9 import android.content.Intent;10 import android.os.Bundle;11 import android.util.Log;12 13 import com.google.android.c2dm.C2DMBaseReceiver;14 //接收C2DM服务器Push的消息,包括注册返回的registration_id消息,推送的数据消息等15 public class C2DMReceiver extends C2DMBaseReceiver{16 17 private static final String TAG="C2DMReceiver";18 //19 public C2DMReceiver()20 {21 super(AndroidC2DMDemo.SENDER_ID);22 }23 public C2DMReceiver(String senderId) {24 super(senderId);25 // TODO Auto-generated constructor stub26 }27 //接收到Push消息的回调函数28 @Override29 protected void onMessage(Context context, Intent intent) {30 // TODO Auto-generated method stub31 Log.v(TAG, "C2DMReceiver message");32 Bundle extras = intent.getExtras();33 if(extras!=null){34 String msg = (String)extras.get(AndroidC2DMDemo.MESSAGE_KEY_ONE);35 Log.v(TAG, "The received msg = "+msg);36 //在标题栏上显示通知37 NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);38 Notification notification = new Notification(R.drawable.icon, msg, System.currentTimeMillis());39 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AndroidC2DMDemo.class), 0);40 notification.setLatestEventInfo(this, getString(R.string.app_name), msg, contentIntent);41 notificationManager.notify(0, notification);42 43 }44 }45 46 @Override47 public void onError(Context context, String errorId) {48 // TODO Auto-generated method stub49 Log.v(TAG, "C2DMReceiver error");50 }51 52 @Override53 public void onRegistered(Context context, String registrationId)54 throws IOException {55 // TODO Auto-generated method stub56 super.onRegistered(context, registrationId);57 Log.v(TAG, "C2DMReceiver Register");58 }59 @Override60 public void onUnregistered(Context context) {61 // TODO Auto-generated method stub62 super.onUnregistered(context);63 Log.v(TAG, "C2DMReceiver UnRegister");64 } 65 }在这个类中我们主要在接收到Push的回调函数onMessage中对消息进行了接收,并且使用Notification的方式显示在状态栏上。
我们完整的工程目录是这样的:
图4 工程目录
最后我们还要在AndroidManifest.xml中增加对应的权限等内容:
AndroidManifest.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.ichliebephone.c2dm" 4 android:versionCode="1" 5 android:versionName="1.0"> 6 <uses-sdk android:minSdkVersion="8" /> 7 8 <!--Only this application can receive the message and registration result --> 9 <!-- 设置一个权限,使只有这个应用才能接收到对应Push的消息及注册时返回的结果 -->10 <permission android:name="com.ichliebephone.c2dm.permission.C2D_MESSAGE"11 android:protectionLevel="signature"></permission>12 <uses-permission android:name="com.ichliebephone.c2dm.permission.C2D_MESSAGE"/>13 <!-- This application has the permission to register and receive c2dm message -->14 <!-- 设置注册和接收C2DM Push消息的权限 -->15 <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />16 <!-- Send and registration id to the server -->17 <!-- 设置联网权限,在把registration_id发送给服务器的时候要用 -->18 <uses-permission android:name="android.permission.INTERNET" />19 <!-- App must have this permission to use the library -->20 <!-- 其他和获取手机中用户账户相关的权限 -->21 <uses-permission android:name="android.permission.WAKE_LOCK" />22 <uses-permission android:name="android.permission.GET_ACCOUNTS" />23 <uses-permission android:name="android.permission.USE_CREDENTIALS" />24 25 <application android:icon="@drawable/icon" android:label="@string/app_name">26 <activity android:name=".AndroidC2DMDemo"27 android:label="@string/app_name">28 <intent-filter>29 <action android:name="android.intent.action.MAIN" />30 <category android:name="android.intent.category.LAUNCHER" />31 </intent-filter>32 </activity>33 34 <!-- In order to use the c2dm library, an35 application must declare a class with the name C2DMReceiver, in its 36 own package, extending com.google.android.c2dm.C2DMBaseReceiver 37 It must also include this section in the manifest. -->38 <!-- 为了使用c2dm包com.google.android.c2dm及其对应的3个类,我们需要声明一个39 继承com.google.android.c2dm.C2DMBaseReceiver类的子类C2DMReceiver,40 并且要在这声明下 --> 41 <service android:name=".C2DMReceiver" />42 43 <!-- Only google service can send data messages for the app. If permission is not set -44 any other app can generate it --> 45 <!-- 谷歌的C2DM服务只为这个程序发送数据,声明对应的权限 -->46 <receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"47 android:permission="com.google.android.c2dm.permission.SEND">48 <!-- Receive the actual message -->49 <!-- 可以接收实际的Push数据 -->50 <intent-filter>51 <action android:name="com.google.android.c2dm.intent.RECEIVE" />52 <category android:name="com.ichliebephone.c2dm" />53 </intent-filter>54 <!-- Receive the registration id -->55 <!-- 可以接收注册后返回的registration_id -->56 <intent-filter>57 <action android:name="com.google.android.c2dm.intent.REGISTRATION" />58 <category android:name="com.ichliebephone.c2dm" />59 </intent-filter>60 </receiver>61 </application>62 </manifest>因为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终端下输入:
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内容:
1 SID=DQAAAKYAAADcTtHbBBNcZJEOfkfVRycD_ZOIidwsQ3UwIY7cSrYWaY6uhlfo0l9gRPB-mQxP4K2T5tWiG--vWVmSTeq5p8SPwgnsYvfzj7bkNiPPIy4xRimVVfBmAHnZgLohw7gHMKi5DS6kK-Ut5tNzdTkI0I2tUDF0ryQ7MnPpI6Sj-gUCyBXmvKatHHDnNTTV78XdGIx7FYej1DyqGsPsYo3bCstHgltjv3cd2Hs7D4yrpUWHZw2 LSID=DQAAAKgAAABCpaoUE4XvxM24Cofntw1IUGx5fKxX-m7aqTL0zhunP0OjzJ2sn9ywmPa1BMZ2cF2IchuxHFLVzaSQfydAmiHZJGXLgaUorpIN6yz1e0VFWKmS6j4wGjZOos3QoJ9rkha0jKbOiHfBesADjxk-qjJ24TJ0RL-xkZHQyzS69YlA1KyzqIKjAMCzgqaDfCwhqxylJzizJksO2h8xpAFXZ38d_grm8XYZtzejiCiAMAR65A3 Auth=DQAAAKoAAACRF4pgYULnXULoWgbwfdqmMiRhfZYa1l-LW_rwGD7cofov4L4c2bVrtCOXbEbkju_hhqdAonpMkrb5icptt28fU8c-s-u1y2MXNYDxPIdQzfA2t6oI3NTmyj35MpsR1NKL4TN7ZVEn6z9NueuiKAqLHukZYh1YMGkGC8M6rVvA7AWPW36064XCQED7KLVNp_pGT00lrni7UdZKZWEy0FT-EVR-OxDyHWw6C-5Kmfkisw返回的内容包括SID,LSID和Auth三个部分,其中Auth是我们需要的内容。
有了Auth和registration_id值后,我们就可以继续用curl命令模拟我们自己服务器的功能向C2DM发送要推送的数据:
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值,比如:
1 id=0:1308623423080544l5c15c2000000312 lingaohe@lingaohe-laptop:~$这时我们的服务器就已经把数据发送给C2DM服务器了,Android设备上一会就能接收到C2DM服务器Push的数据。
在我们的例子中我们可以看到DDMS中打印出的消息:
图7 获取到的Push数据
同时Android模拟器的状态栏上会有对应的通知显示:
图8 Android模拟器接收到的Push数据
这样我们就快速实现了下Android的C2DM框架的Push功能。进一步的具体解释说明及服务器端的代码处理我们以后再学习。
文章对应的完整代码例子下载地址:
http://download.csdn.net/source/3425855
转载自:http://blog.csdn.net/ichliebephone/article/details/6591071
转载于:https://www.cnblogs.com/iOcean/archive/2011/12/07/2279170.html
相关资源:JAVA上百实例源码以及开源项目