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過程圖來說明下。

圖1 C2DM操作過程圖

要使用C2DM來進行Push操作,基本上要使用以下6個步驟:

         (1)註冊:Android設備把使用C2DM功能的用戶賬戶(比如[email protected])和App名稱發送給C2DM服務器。

         (2)C2DM服務器會返回一個registration_id值給Android設備,設備需要保存這個registration_id值。

         (3)Android設備把獲得的registration_id和C2DM功能的用戶賬戶([email protected])發送給自己的服務器,不過一般用戶賬戶信息因爲和服務器確定好的,所以不必發送。

這樣Android設備就完成了C2DM功能的註冊過程,接下來就可以接收C2DM服務器Push過來的消息了。

         (4)服務器獲得數據。這裏圖中的例子Chrome To Phone,服務器接收到Chrome瀏覽器發送的數據。數據也可以是服務器本地產生的。這裏的服務器是Google AppEngine(很好的一項服務,可惜在國內被屏了),要換成自己的服務器。服務器還要獲取註冊使用C2DM功能的用戶賬戶([email protected])的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服務。我們這裏使用的是專門註冊的[email protected]郵箱。

         提交後,過一段時間就會收到Google發送過來的確認郵件,然後你就可以使用C2DM的Push服務了。

        

介紹了這麼多,我們先來快速完成一個實例,只完成Android設備端的註冊部分,不包含向服務器發送registration_id和服務器向C2DM服務器發送數據的具體代碼,這部分只是用Ubuntu下的curl命令來模擬,主要是快速親自體驗一下Push的結果。

創建一個Android工程AndroidC2DMDemo,並且包含進Google的開源例子Chrome To Phone中的c2dm包com.google.android.c2dm,包中包含三個Java類,分別爲:

第一個類爲C2DMBaseReceiver:

/*
 * 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:

/*
 */
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:

/*
 * 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:

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 = "[email protected]"; //使用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,如下圖所示:

圖5 設置Android設備中的Google賬戶

然後就可以運行程序了,我們會在DDMS輸出中看到獲得的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 protected]&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中打印出的消息:

圖7 獲取到的Push數據

同時Android模擬器的狀態欄上會有對應的通知顯示:

圖8 Android模擬器接收到的Push數據

       這樣我們就快速實現了下Android的C2DM框架的Push功能。進一步的具體解釋說明及服務器端的代碼處理我們以後再學習。

 

文章對應的完整代碼例子下載地址:

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

注:文章參加第二屆 Google 暑期大學生博客分享大賽 - 2011 Android 成長篇 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章