Android C2DM學習——雲端推送

一.基礎知識

當咱們開發須要和服務器交互的應用程序時,基本上都須要獲取服務器端的數據,好比《地震及時通》就須要及時獲取服務器上最新的地震信息。要獲取服務器上不定時更新的信息通常來講有兩種方法,第一種是客戶端使用Pull(拉)的方式,隔一段時間就去服務器上獲取信息,看是否有更新的信息出現。第二種就是服務器使用Push(推送)的方式,當服務器端有新信息了,則把最新的信息Push到客戶端上。 html

雖然Pull和Push兩種方式都能實現獲取服務器端更新信息的功能,可是明顯來講Push is better than pull。由於Pull方式更費客戶端的網絡流量,更主要的是費電量。 java

Android從2.2版本開始增長了Cloud to Device Messaging(C2DM)框架,在系統中支持了Push功能,基於Android平臺使用Push功能更加簡單了。雖然C2DM目前還處在實驗室階段,不太小規模的使用應該沒問題 android

下面咱們就來體驗一下Android的C2DM功能。 chrome

二.C2DM框架

使用Android的C2DM功能有幾個要求: express

1.       須要Android2.2及以上的系統版本。 apache

2.       使用C2DM功能的Android設備上須要設置好Google的帳戶。 api

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:

[java]  view plain copy
  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 saved  
  105.     }  
  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.     @Override  
  118.     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_id  
  172.         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 unregistered  
  180.             C2DMessaging.clearRegistrationId(context);  
  181.             onUnregistered(context);  
  182.             return;  
  183.         } else if (error != null) {  
  184.             // we are not registered, can try again  
  185.             C2DMessaging.clearRegistrationId(context);  
  186.             // Registration failed  
  187.             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:

[java]  view plain copy
  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 activity 
  14.  * - 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.     @Override  
  23.     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:

[java]  view plain copy
  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, 0new 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.                 0new 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.     // package  
  113.     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.     // package  
  125.     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:

[java]  view plain copy
  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";   //和服務器商量好的接收消息的鍵值key  
  14.       
  15.     @Override  
  16.     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:

[java]  view plain copy
  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 stub  
  26.     }  
  27.     //接收到Push消息的回調函數  
  28.     @Override  
  29.     protected void onMessage(Context context, Intent intent) {  
  30.         // TODO Auto-generated method stub  
  31.         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(this0new 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.     @Override  
  47.     public void onError(Context context, String errorId) {  
  48.         // TODO Auto-generated method stub  
  49.         Log.v(TAG, "C2DMReceiver error");  
  50.     }  
  51.       
  52.     @Override  
  53.     public void onRegistered(Context context, String registrationId)  
  54.             throws IOException {  
  55.         // TODO Auto-generated method stub  
  56.         super.onRegistered(context, registrationId);  
  57.         Log.v(TAG, "C2DMReceiver Register");  
  58.     }  
  59.     @Override  
  60.     public void onUnregistered(Context context) {  
  61.         // TODO Auto-generated method stub  
  62.         super.onUnregistered(context);  
  63.         Log.v(TAG, "C2DMReceiver UnRegister");  
  64.     }     
  65. }  


 

在這個類中咱們主要在接收到Push的回調函數onMessage中對消息進行了接收,而且使用Notification的方式顯示在狀態欄上。

咱們完整的工程目錄是這樣的:

圖4 工程目錄

         最後咱們還要在AndroidManifest.xml中增長對應的權限等內容:

[html]  view plain copy
  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, an  
  35.              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終端下輸入:

[plain]  view plain copy
  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內容:

[plain]  view plain copy
  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發送要推送的數據:

[plain]  view plain copy
  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值,好比:

[plain]  view plain copy
  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

相關文章
相關標籤/搜索