zoukankan      html  css  js  c++  java
  • Android C2DM学习

    一、基础知识

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

    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。

    注册C2DM账户
    图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:

    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:

    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:

    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:

    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:

    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中增加对应的权限等内容:

    <?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,如下图所示:

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

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

    获得的registration_id
    图6 获得的registration_id

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

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

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

    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内容:

    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发送要推送的数据:

    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值,比如:

    id=0:1308623423080544%6c5c15c200000031
    lingaohe@lingaohe-laptop:~$

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

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

    获取到的Push数据
    图7 获取到的Push数据

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

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

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

  • 相关阅读:
    AX 2012 Security Framework
    The new concept 'Model' in AX 2012
    How to debug the SSRS report in AX 2012
    Using The 'Report Data Provider' As The Data Source For AX 2012 SSRS Report
    Deploy SSRS Report In AX 2012
    AX 2012 SSRS Report Data Source Type
    《Taurus Database: How to be Fast, Available, and Frugal in the Cloud》阅读笔记
    图分析理论 大纲小结
    一文快速了解Posix IO 缓冲
    #转载备忘# Linux程序调试工具
  • 原文地址:https://www.cnblogs.com/xiaochao1234/p/3701645.html
Copyright © 2011-2022 走看看