Android C2DM学习——云端推送

2023-11-19

一.基础知识

当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如《地震及时通》就需要及时获取服务器上最新的地震信息。要获取服务器上不定时更新的信息一般来说有两种方法,第一种是客户端使用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:

  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. }  
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.c2dm;

import java.io.IOException;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;

/**
 * Base class for C2D message receiver. Includes constants for the
 * strings used in the protocol.
 */
/**
 * 接收和处理C2DM消息的基类
 * */
public abstract class C2DMBaseReceiver extends IntentService {
	//和C2DM Push的Intent内容相关
	//重新向C2DM服务器注册
    private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";   
    //向C2DM服务器注册后的回调处理
    public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";  
    //接收到C2DM服务器的推送消息
    private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";

    // Logging tag
    private static final String TAG = "C2DM";

    // Extras in the registration callback intents.
    //向C2DM注册返回的intent中包含的key
    public static final String EXTRA_UNREGISTERED = "unregistered";
    public static final String EXTRA_ERROR = "error";
    public static final String EXTRA_REGISTRATION_ID = "registration_id";
    //向C2DM注册出错的原因
    public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
    public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
    public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
    public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
    public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
    public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
    public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
    
    // wakelock
    private static final String WAKELOCK_KEY = "C2DM_LIB";

    private static PowerManager.WakeLock mWakeLock;
    private final String senderId;

    /**
     * The C2DMReceiver class must create a no-arg constructor and pass the 
     * sender id to be used for registration.
     */
    public C2DMBaseReceiver(String senderId) {
        // senderId is used as base name for threads, etc.
        super(senderId);
        this.senderId = senderId;
    }
    //下面几个是接收到C2DM Push过来的信息后的回调函数,都可以在继承的子类中处理
    /**
     * Called when a cloud message has been received.
     */
    /**
     * 接收到C2DM服务器Push的消息后的回调函数,需要在继承的子类中处理
     * */
    protected abstract void onMessage(Context context, Intent intent);

    /**
     * Called on registration error. Override to provide better
     * error messages.
     *  
     * This is called in the context of a Service - no dialog or UI.
     */
    /**
     * 出错的回调函数
     * */
    public abstract void onError(Context context, String errorId);

    /**
     * Called when a registration token has been received.
     */
    /**
     * 注册后的回调函数
     * */
    public void onRegistered(Context context, String registrationId) throws IOException {
        // registrationId will also be saved
    }

    /**
     * Called when the device has been unregistered.
     */
    /**
     * 取消注册的回调函数
     * */
    public void onUnregistered(Context context) {
    }

    //IntentService的方法
    @Override
    public final void onHandleIntent(Intent intent) {
        try {
            Context context = getApplicationContext();
            if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
                handleRegistration(context, intent);//处理注册后的回调
            } else if (intent.getAction().equals(C2DM_INTENT)) {
                onMessage(context, intent);//处理C2DM Push消息的回调
            } else if (intent.getAction().equals(C2DM_RETRY)) {
                C2DMessaging.register(context, senderId); //重新注册
            }
        } finally {
            //  Release the power lock, so phone can get back to sleep.
            // The lock is reference counted by default, so multiple 
            // messages are ok.
            
            // If the onMessage() needs to spawn a thread or do something else,
            // it should use it's own lock.
            mWakeLock.release();
        }
    }

    
    /**
     * Called from the broadcast receiver. 
     * Will process the received intent, call handleMessage(), registered(), etc.
     * in background threads, with a wake lock, while keeping the service 
     * alive. 
     */
    static void runIntentInService(Context context, Intent intent) {
        if (mWakeLock == null) {
            // This is called from BroadcastReceiver, there is no init.
            PowerManager pm = 
                (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 
                    WAKELOCK_KEY);
        }
        mWakeLock.acquire();
       
        // Use a naming convention, similar with how permissions and intents are 
        // used. Alternatives are introspection or an ugly use of statics. 
        String receiver = context.getPackageName() + ".C2DMReceiver";
        intent.setClassName(context, receiver);
        
        context.startService(intent);

    }
    
    //处理注册后的回调
    private void handleRegistration(final Context context, Intent intent) {
        final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
        String error = intent.getStringExtra(EXTRA_ERROR);
        String removed = intent.getStringExtra(EXTRA_UNREGISTERED);
        Log.v(TAG, "handleRegistration");
        //打印出接收到的registraton_id
        Log.v(TAG, "dmControl: registrationId = " + registrationId +
                ", error = " + error + ", removed = " + removed);
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "dmControl: registrationId = " + registrationId +
                ", error = " + error + ", removed = " + removed);
        }
        if (removed != null) {
            // Remember we are unregistered
            C2DMessaging.clearRegistrationId(context);
            onUnregistered(context);
            return;
        } else if (error != null) {
            // we are not registered, can try again
            C2DMessaging.clearRegistrationId(context);
            // Registration failed
            Log.e(TAG, "Registration error " + error);
            onError(context, error);
            if ("SERVICE_NOT_AVAILABLE".equals(error)) {
                long backoffTimeMs = C2DMessaging.getBackoff(context);
                
                Log.d(TAG, "Scheduling registration retry, backoff = " + backoffTimeMs);
                Intent retryIntent = new Intent(C2DM_RETRY);
                PendingIntent retryPIntent = PendingIntent.getBroadcast(context, 
                        0 /*requestCode*/, retryIntent, 0 /*flags*/);
                
                AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.ELAPSED_REALTIME,
                        backoffTimeMs, retryPIntent);

                // Next retry should wait longer.
                backoffTimeMs *= 2;
                C2DMessaging.setBackoff(context, backoffTimeMs);
            } 
        } else {
            try {
                onRegistered(context, registrationId);
                C2DMessaging.setRegistrationId(context, registrationId);
            } catch (IOException ex) {
                Log.e(TAG, "Registration error " + ex.getMessage());
            }
        }
    }
}


 

第二个类为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 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. }  
/*
 */
package com.google.android.c2dm;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * Helper class to handle BroadcastReciver behavior.
 * - can only run for a limited amount of time - it must start a real service 
 * for longer activity
 * - must get the power lock, must make sure it's released when all done.
 * 
 */
/**
 * 帮助类,帮忙处理BroadcastReciver过程
 * */
public class C2DMBroadcastReceiver extends BroadcastReceiver {
    
    @Override
    public final void onReceive(Context context, Intent intent) {
        // To keep things in one place.
        C2DMBaseReceiver.runIntentInService(context, intent);
        setResult(Activity.RESULT_OK, null /* data */, null /* extra */);        
    }
}


 

第三个类为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, 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. }  
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.c2dm;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * Utilities for device registration.
 *
 * Will keep track of the registration token in a private preference.
 */
/**
 * 和注册相关的一些实用函数
 * */
public class C2DMessaging {
    public static final String EXTRA_SENDER = "sender";
    public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
    public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
    public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
    public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
    public static final String BACKOFF = "backoff";
    public static final String GSF_PACKAGE = "com.google.android.gsf";  //GSF为GoogleServicesFramework.apk的缩写


    // package
    static final String PREFERENCE = "com.google.android.c2dm";
    
    private static final long DEFAULT_BACKOFF = 30000;

    /**
     * Initiate c2d messaging registration for the current application
     */
    public static void register(Context context,
            String senderId) {
        Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
        registrationIntent.setPackage(GSF_PACKAGE);
        registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
                PendingIntent.getBroadcast(context, 0, new Intent(), 0));
        registrationIntent.putExtra(EXTRA_SENDER, senderId);
        context.startService(registrationIntent);
        // TODO: if intent not found, notification on need to have GSF
    }

    /**
     * Unregister the application. New messages will be blocked by server.
     */
    public static void unregister(Context context) {
        Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
        regIntent.setPackage(GSF_PACKAGE);
        regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context,
                0, new Intent(), 0));
        context.startService(regIntent);
    }

    /**
     * Return the current registration id.
     *
     * If result is empty, the registration has failed.
     *
     * @return registration id, or empty string if the registration is not complete.
     */
    public static String getRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        String registrationId = prefs.getString("dm_registration", "");
        return registrationId;
    }

    public static long getLastRegistrationChange(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
    }
    
    static long getBackoff(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
    }
    
    static void setBackoff(Context context, long backoff) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putLong(BACKOFF, backoff);
        editor.commit();

    }

    // package
    static void clearRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", "");
        editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
        editor.commit();

    }

    // package
    static void setRegistrationId(Context context, String registrationId) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", registrationId);
        editor.commit();

    }
}


 

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

然后创建我们自己的包com.ichliebephone.c2dm,包含两个类,一个是工程的入口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";   //和服务器商量好的接收消息的键值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. }  
package com.ichliebephone.c2dm;

import com.google.android.c2dm.C2DMessaging;

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

public class AndroidC2DMDemo extends Activity {
    /** Called when the activity is first created. */
	private static final String TAG = "AndroidC2DMDemo";
	public static final String SENDER_ID = "android.c2dm.demo@gmail.com"; //使用C2DM服务的用户账户
	public static final String MESSAGE_KEY_ONE = "msg";   //和服务器商量好的接收消息的键值key
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Log.v(TAG, "Start");
        //向C2DM服务器注册
        C2DMessaging.register(this, SENDER_ID);
    }
    
    
}


 

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

另一个类为C2DMBaseReceiver的子类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 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. }  
package com.ichliebephone.c2dm;

import java.io.IOException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.google.android.c2dm.C2DMBaseReceiver;
//接收C2DM服务器Push的消息,包括注册返回的registration_id消息,推送的数据消息等
public class C2DMReceiver extends C2DMBaseReceiver{
	
	private static final String TAG="C2DMReceiver";
	//
	public C2DMReceiver()
	{
		super(AndroidC2DMDemo.SENDER_ID);
	}
	public C2DMReceiver(String senderId) {
		super(senderId);
		// TODO Auto-generated constructor stub
	}
	//接收到Push消息的回调函数
	@Override
	protected void onMessage(Context context, Intent intent) {
		// TODO Auto-generated method stub
		Log.v(TAG, "C2DMReceiver message");
		Bundle extras = intent.getExtras();
		if(extras!=null){
			String msg = (String)extras.get(AndroidC2DMDemo.MESSAGE_KEY_ONE);
			Log.v(TAG, "The received msg = "+msg);
			//在标题栏上显示通知
			NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
			Notification notification = new Notification(R.drawable.icon, msg, System.currentTimeMillis());
			PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AndroidC2DMDemo.class), 0);
			notification.setLatestEventInfo(this, getString(R.string.app_name), msg, contentIntent);
			notificationManager.notify(0, notification);
			
		}
	}

	@Override
	public void onError(Context context, String errorId) {
		// TODO Auto-generated method stub
		Log.v(TAG, "C2DMReceiver error");
	}
	
	@Override
	public void onRegistered(Context context, String registrationId)
			throws IOException {
		// TODO Auto-generated method stub
		super.onRegistered(context, registrationId);
		Log.v(TAG, "C2DMReceiver Register");
	}
	@Override
	public void onUnregistered(Context context) {
		// TODO Auto-generated method stub
		super.onUnregistered(context);
		Log.v(TAG, "C2DMReceiver UnRegister");
	}	
}


 

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

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

图4 工程目录

         最后我们还要在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, 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>  
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.ichliebephone.c2dm"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

	<!--Only this application can receive the message and registration result  -->
	<!-- 设置一个权限,使只有这个应用才能接收到对应Push的消息及注册时返回的结果  -->
	<permission android:name="com.ichliebephone.c2dm.permission.C2D_MESSAGE"
		android:protectionLevel="signature"></permission>
	<uses-permission android:name="com.ichliebephone.c2dm.permission.C2D_MESSAGE"/>
	<!-- This application has the permission to register and receive c2dm message -->
	<!-- 设置注册和接收C2DM Push消息的权限 -->
	<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
	<!-- Send and registration id to the server  -->
	<!-- 设置联网权限,在把registration_id发送给服务器的时候要用 -->
	<uses-permission android:name="android.permission.INTERNET" />
	<!-- App must have this permission to use the library -->
	<!-- 其他和获取手机中用户账户相关的权限 -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".AndroidC2DMDemo"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
		
		<!-- In order to use the c2dm library, an
             application must declare a class with the name C2DMReceiver, in its 
             own package, extending com.google.android.c2dm.C2DMBaseReceiver               
             It must also include this section in the manifest. -->
        <!-- 为了使用c2dm包com.google.android.c2dm及其对应的3个类,我们需要声明一个
        	继承com.google.android.c2dm.C2DMBaseReceiver类的子类C2DMReceiver,
        	并且要在这声明下 -->     
        <service android:name=".C2DMReceiver" />
                
        <!-- Only google service can send data messages for the app. If permission is not set -
             any other app can generate it --> 
        <!-- 谷歌的C2DM服务只为这个程序发送数据,声明对应的权限 -->
        <receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
                  android:permission="com.google.android.c2dm.permission.SEND">
            <!-- Receive the actual message -->
            <!-- 可以接收实际的Push数据 -->
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="com.ichliebephone.c2dm" />
            </intent-filter>
            <!-- Receive the registration id -->
            <!-- 可以接收注册后返回的registration_id -->
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="com.ichliebephone.c2dm" />
            </intent-filter>
        </receiver>
    </application>
</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  
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-gUCyBXmvKatHHDnNTTV78XdGIx7FYej1DyqGsPsYo3bCstHgltjv3cd2Hs7D4yrpUWHZw  
  2. LSID=DQAAAKgAAABCpaoUE4XvxM24Cofntw1IUGx5fKxX-m7aqTL0zhunP0OjzJ2sn9ywmPa1BMZ2cF2IchuxHFLVzaSQfydAmiHZJGXLgaUorpIN6yz1e0VFWKmS6j4wGjZOos3QoJ9rkha0jKbOiHfBesADjxk-qjJ24TJ0RL-xkZHQyzS69YlA1KyzqIKjAMCzgqaDfCwhqxylJzizJksO2h8xpAFXZ38d_grm8XYZtzejiCiAMAR65A  
  3. Auth=DQAAAKoAAACRF4pgYULnXULoWgbwfdqmMiRhfZYa1l-LW_rwGD7cofov4L4c2bVrtCOXbEbkju_hhqdAonpMkrb5icptt28fU8c-s-u1y2MXNYDxPIdQzfA2t6oI3NTmyj35MpsR1NKL4TN7ZVEn6z9NueuiKAqLHukZYh1YMGkGC8M6rVvA7AWPW36064XCQED7KLVNp_pGT00lrni7UdZKZWEy0FT-EVR-OxDyHWw6C-5Kmfkisw  
SID=DQAAAKYAAADcTtHbBBNcZJEOfkfVRycD_ZOIidwsQ3UwIY7cSrYWaY6uhlfo0l9gRPB-mQxP4K2T5tWiG--vWVmSTeq5p8SPwgnsYvfzj7bkNiPPIy4xRimVVfBmAHnZgLohw7gHMKi5DS6kK-Ut5tNzdTkI0I2tUDF0ryQ7MnPpI6Sj-gUCyBXmvKatHHDnNTTV78XdGIx7FYej1DyqGsPsYo3bCstHgltjv3cd2Hs7D4yrpUWHZw
LSID=DQAAAKgAAABCpaoUE4XvxM24Cofntw1IUGx5fKxX-m7aqTL0zhunP0OjzJ2sn9ywmPa1BMZ2cF2IchuxHFLVzaSQfydAmiHZJGXLgaUorpIN6yz1e0VFWKmS6j4wGjZOos3QoJ9rkha0jKbOiHfBesADjxk-qjJ24TJ0RL-xkZHQyzS69YlA1KyzqIKjAMCzgqaDfCwhqxylJzizJksO2h8xpAFXZ38d_grm8XYZtzejiCiAMAR65A
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  
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:1308623423080544%6c5c15c200000031  
  2. lingaohe@lingaohe-laptop:~$  
id=0:1308623423080544%6c5c15c200000031
lingaohe@lingaohe-laptop:~$


 

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

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

图7 获取到的Push数据

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

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

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

 

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android C2DM学习——云端推送 的相关文章

  • 微信小程序报错-errCode: -1

    1 报错截图 2 报错原因 Collection remove 需要小程序端2 9 4版本或之后的版本才支持 看了眼我的调试基础库 我的版本是2 8 1 所以 3 解决方法 点击右上角的详情 本地设置 调试基础库选择2 14 0
  • js数组相加相减函数

    数组相减 reduceArray arr1 arr2 for var i arr1 length 1 i gt 0 i var a arr1 i for var j arr2 length 1 j gt 0 j var b arr2 j i
  • Qt打开包含头文件以及打开函数声明和定义的方法

    当第一次拿到被人的程序的时候不知道如何向VS或者keil这样的编译器可以直接右键打开头文件 Qt可以通过 1 鼠标放在你需要打开的头文件或者函数声明的地方 如下图我鼠标放在MainWindow上 2 Ctrl 鼠标左键单击 或者使用快捷键F
  • python-算法时间复杂度和空间复杂度

    大O表示法 O 名称 举例 1 常量时间 一次赋值 logn 对数时间 折半查找 n 线性时间 线性查找 nlogn 对数线性时间 快速排序 n 2 平方 两重循环 n 3 立方 三重循环 2 n 指数 递归求斐波那契数列 n 阶乘 旅行商
  • html文本元素

    文章目录 h p span pre code 实体字符 strong i em del s h h head 标题 一共有六级标题 hKaTeX parse error Expected got EOF at end of input 6
  • 【编译原理】 CS143 斯坦福大学公开课 第一周:简介

    youtube 1 1 Introduction to Compilers and interpreters 1 1 Introduction to Compilers and interpreters 编译器解释器介绍 两种主要的实现编程
  • three.js中聚光灯及其属性介绍

    一 聚光灯及其属性介绍 Three js中的聚光灯 SpotLight 是一种用于在场景中创建聚焦光照的光源类型 它有以下属性 color 聚光灯的颜色 intensity 聚光灯的强度 distance 聚光灯的有效距离 angle 聚光
  • [毕业设计]2023-2024年最新电子科学与技术专业毕设选题题目推荐汇总

    文章目录 1前言 2 如何选题 3 选题方向 3 1 嵌入式开发方向 3 2 物联网方向 3 3 人工智能方向 3 4 算法研究方向 3 5 学长作品展示 4 最后 1前言 近期不少学弟学妹询问学长关于电子科学与技术专业相关的毕设选题 学长
  • java如何检测连接池连接情况,如何检查是否使用了连接池

    I use HSQLDB EclipseLink Gemini on OSGI framework Felix In spite that I ve set pool in persistence xml I have serious su
  • 全网最全谷粒商城记录_01、简介-项目介绍(2022-07-06更新完成)

    声明 本教程不收取任何费用 欢迎转载 尊重作者劳动成果 不得用于商业用途 侵权必究 目录 分布式基础 全栈开发篇 分布式高级 微服务架构篇 高可用集群 架构师提升篇 希望大家 微服务架构图简单介绍 项目简介 1 项目背景 1 电商模式 1
  • JavaWeb学习笔记-part1

    互联网通信 什么是互联网通信 两台计算机通过网络实现文件共享行为 就是互联网通信 互联网通信中的角色划分 客户端 用于发送请求的计算机 服务端 用于接受请求 并满足请求的计算机 互联网通信模型 C S通信模型 client software
  • Handler机制与原理

    为什么会出现内存泄漏问题呢 分析 Handler使用是用来进行线程间通信的 所以新开启的线程是会持有Handler引用的 如果在Activity等中创建Handler 并且是非静态内部类的形式 就有可能造成内存泄漏 非静态内部类是会隐式持有
  • uniapp 开发微信小程序之新版隐私协议

    自从微信小程序官方更新隐私协议 用户必须同意之后 才能获取个人信息 这就导致在获取用户信息之前 需要有个隐私协议弹窗 大致如下图 微信小程序官方提供的API和 uniapp 开发的稍微有点区别 这里只记录 uniapp 开发的 如果需要微信
  • 高中学历的程序员,以包装的方式进入现在的公司,想跳槽咋办?

    网友自述 我在现在广州这家公司工作了两年 技术上有一定提升 但这两年我过得一直不是很快乐 因为我学历包装 所以我不敢跟同事交往太深 一直孤身一人 非常难受 可能这就是代价吧 现在我想换一个公司 我不想再用假身份了 但不知道用高中学历是否能够
  • Java对点、线、面生成栅格瓦片jpg,并渲染呈现

    Java对点 线 面生成栅格瓦片jpg 并渲染呈现 1 效果图 2 原理 2 1 面瓦片的生成 2 2 线瓦片的生成 2 3 多点瓦片的生成 3 源码 参考 这篇博客将介绍从前端HTML页面到后端预生成栅格瓦片jpg 并提供查询接口供前端h
  • Python文件操作

    1 with open E 信息 docx rb as f 2 read data f read 3 f closed rb 以二进制形式读取指定路径的文件 再以二进制形式写入指定路径 wb 1 with open E 信息 2 docx
  • Go Web编程实战(6)----反射

    目录 反射 反射的3大原则 接口类型变量 转换为 反射类型对象 反射类型对象 转换为 接口类型变量 反射类型对象 修改 值必 可写的 反射 与其他语言一样 Go语言的反射同样是指 计算机程序在运行时 可以访问 检测和修改它本身状态或行为的一

随机推荐

  • MAC 怎么终端怎么退出和进入Anaconda环境

    mac安装完anaconda 后 命令行窗口默认使用conda的 取消默认 用以下一行代码在命令行运行即可 重启终端 conda config set auto activate base false 将false改为true设置默认环境为
  • Codeforces 1475C. Ball in Berland(二元容斥)

    题目传送门 题意 一个班级有a个男生和b个女生 现在这个班级有k对男女愿意一起出席毕业典礼 这里注意k对男女中可能会有某个男生或女生出现在多个pair中 你从这k对中找出两对 使得这两对中的男生不相同 女生不相同 即一个男生或女生不可能在一
  • cuda 矩阵乘法,从最容易理解到算得最快(第二版源码-tile机制+共享内存)

    下面我们仅仅引入tiling方法 在共享内存中进行分块矩阵的乘法运算 先分析一下能够减少多少次对全局存储区的访问 当M N K 4096时 用第一版的代码 忽略cache的缓存时 需要从全局存储区读取2 4096 3 个float变量 为了
  • 法拉利虚拟学院2010 服务器,法拉利虚拟学院2010

    意大利著名好车品牌 法拉利 一直在世界上享受名誉 该游戏作品将带领玩家感悟法拉利的文化底蕴 游戏介绍 法拉利虚拟学院2010 包括了2010款法拉利F1赛车F10 以及三条通过镭射扫描技术绘制的高精度赛道 Fiorano Mugello N
  • spring boot 简介以及作用

    我们都知道spring是一个功能非常强大的框架 但是它也存在非常不好的弱点 也是对于我们普通的程序员的致命的弱点 就是它的配置文件太多了 而 在开发界一直有一句话 就是约定大于配置 这样一句话 就是说系统 类库 框架应该假定合理的默认值 而
  • JsonObject对象和jsonArrsy数组的获取JDK1.8,添加到表中

    1 基础数据结构 一个合同号对应多个批号 一个批号对应多个车辆 arrivalReport contractContent contractNumber 2021 11 17合同号 orderNumber 2021 11 17 0032订单
  • AbstractQueuedSynchronizer之AQS

    文章目录 AbstractQueuedSynchronizer AQS 概述 基本原理 实现细节 等待队列 state属性 独占模式 ReentrantLock AbstractQueuedSynchronizer AQS 概述 Abstr
  • 爬虫学习笔记(十五)——加密解密

    文章目录 一 概念和作用 1 1 概念 1 2 作用 1 3 常用加密方式 二 字符编码 2 1 进制间转换方法 python 2 2 unicode 三 Base64编码原理 3 1 概念 3 2 作用 3 3 Base64编码表 3 4
  • MySQL备份与恢复

    2 3 1备份MySQL数据库 在MySQL的bin目录下 有一个名为mysqldump的可执行文件 将该bin目录添加到环境变量中 可以利用它在 命令提示符 环境下来备份数据库 语法格式如下 mysqldump opt 要备份的数据库名
  • 词项(term)与词条(token)区别

    传送门
  • Sublime Text 3 全程详细图文教程(转载)

    今天被群里大佬安利了一款文本编辑软件 找了一下相关教程 一 前言 使用Sublime Text 也有几个年头了 版本也从2升级到3了 但犹如寒天饮冰水 冷暖尽自知 最初也是不知道从何下手 满世界地查找资料 但能查阅到的资料 苦于它们的零碎
  • 系统CPU飙高和频繁GC,我要怎么排查

    1 Full GC次数过多 相对来说 这种情况是最容易出现的 尤其是新功能上线时 对于Full GC较多的情况 其主要有如下两个特征 线上多个线程的CPU都超过了100 通过jstack命令可以看到这些线程主要是垃圾回收线程 通过jstat
  • 理解图像的傅里叶变换(细心分析)

    最近在看图像的傅里叶变换 看着频谱图一直没看明白到底为啥是那样的 跟同学研究了好久 终于想明白了 感谢同学的耐心指导 大家相互讨论真的很快就能出结果 多讨论 多学习 图像的傅里叶变换 图像是一个二维的信号 所以对它进行二维的傅里叶变换 对于
  • layui文件上传接口后端具体实现SpringMVC

    做课程设计时候 用到了layui文件上传接口 参考官方文档给出的响应接口文档 code 0 msg data src http cdn layui com 123 jpg 然后具体的上传书写方式分为前端和后端 layui官方并没有说明上传的
  • [1140]linux查看历史命令history

    一 什么是history 在bash功能中 它能记忆使用过的命令 这个功能最大的好处就是可以查询曾经做过的举动 从而可以知道你的运行步骤 那么就可以追踪你曾下达过的命令 以作为除错的工具 二 History的保存 那么命令记录在哪里呢 在h
  • tomcat数据源配置多个ip oracle,tomcat里配置多数据源(数据库连接池) jndi 和项目连接 ssh框架...

    以mysql和oracle数据库为例 我项目以mysql为主 但需要去一个oracle数据库里查询数据 所以只有mysql里表的实体类 但没有oracle数据库实体类 所以配置mysql的数据源有实体类直接把数据源放到session工厂里用
  • 如何修改文件权限

    改变文件权限的两种方法 第一种 符号 sudo chmod 文件身份 操作符 权限符号 文件档案或目录 文件的四种身份 u user文件所有者 g group 文件所属群组 o other 其他拥有者 a all 全部身份 操作符的三种类
  • 4.1 不定积分的概念与性质

    思维导图 学习目标 学习不定积分 我会采取以下几个步骤 1 学习基本的积分表 首先 我会学习基本的积分公式 例如幂函数 指数函数 三角函数 反三角函数等的积分公式 这些公式是不定积分计算的基础 掌握它们是十分重要的 2 理解积分的定义和性质
  • FLOPS、TOPS和FLOPs的区别

    FLOPS 即每秒浮点运算次数 是每秒所执行的浮点运算次数 Floating point operations per second 缩写 FLOPS 的简称 被用来评估电脑效能 FLOPs 注意s小写 是floating point op
  • Android C2DM学习——云端推送

    一 基础知识 当我们开发需要和服务器交互的应用程序时 基本上都需要获取服务器端的数据 比如 地震及时通 就需要及时获取服务器上最新的地震信息 要获取服务器上不定时更新的信息一般来说有两种方法 第一种是客户端使用Pull 拉 的方式 隔一段时