Writing Android Application that use C2DM
想要写一个使用C2DM的程序,你必须有一个程序服务器端能够执行 Role of the Third-Party Application Server所描述的任务。这一节描述了你创建一个使用C2DM客户端的步骤。
请记住C2DM是没有用户界面的。怎么在程序里处理消息取决于你。
写个程序客户端有两个主要步骤:
1. 创建一个manifest文件。这个文件包含程序使用C2DM需要使用的权限。
2. 写java代码。要使用C2DM,程序要包括:
A. 开始和停止注册服务的代码。
B. Receivers for com.google.android.c2dm.intent.C2D_MESSAGE 和com.google.android.c2dm.intent.REGISTRATION。
Creating the Manifest
每一个程序在根目录下都有一个AndroidManifest.xml文件。这个文件提供程序的必要信息给Android系统,这些信息是系统在运行任何程序代码之前必须要有的。要使用C2DM,这个文件必须包含:
1. com.google.android.c2dm.permission.RECEIVE。程序拥有注册和接受消息的权限。
2. android.permission.INTERNET。程序拥有联网的权限。
3. applicationPackage+”.permission.C2D_MESSAGE”防止其他程序注册和接受这个程序的消息。
4. Receivers for com.google.android.c2dm.intent.RECEIVE和com.google.android.c2dm.intent.REGISTRATION.category设置成applicationPackage。receiver需要com.google.android.c2dm.SEND权限,这样C2DM就可以发送消息给它。Registration和消息接收都是通过Intents来实现的。
5. 如果C2DM对于你的程序是一个至关重要的功能,就一定要在AndroidManifest.xml里设置andorid:minSdkVersion=”8”。这样就确保了程序不会装在一些程序不能正常运行的手机上。
Received C2D_MESSAGE Intents包含第三方服务器发送过来的键值对。一个特别的key是collapse_key。这是发送者设置的允许离线的设备在上线时处理消息。
下面是一个支持C2DM的manifest的例子:
<manifest package="com.example.myapp" ...>
<!—只有这个程序能注册和接受消息 -->
<permission android:name="yourpackagename.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="yourpackagename.permission.C2D_MESSAGE" />
<!—这个程序有注册和接收消息的权限 -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!—发送registration id到服务器需要的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!—只有C2DM服务器可以发送消息给程序. 如果下面的权限不设置,其他的程序也能发送 -->
<receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<!—接收消息 -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="yourpackagename" />
</intent-filter>
<!—接收registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="yourpackagename " />
</intent-filter>
</receiver>
Registering for C2DM
Android程序在接收任何消息前需要向C2DM服务器注册。如果要注册,需要发送一个intent(com.google.android.c2dm.intent.REGISTER),包含2个参数:
1. sender:是一个授权发送消息到程序的ID,通常是程序开发者设置的一个gmail地址。
2. app:application’s ID.通过PendingIntent设置来允许registration service提取程序信息。
比如:
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER"); registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); // boilerplate registrationIntent.putExtra("sender", emailOfSender); startService(registrationIntent);
直到程序把registration ID发送到第三方程序服务器,注册才结束。第三方程序服务器使用这个registration ID发送消息给目标机器上的目标程序。
Unregistering from C2DM
Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER"); unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); startService(unregIntent);
Handling Registration Results
在AndroidManifest.xml里,定义了一个receiver:com.google.android.c2dm.intent.REGISTRATION。同样定义了一个receiver:com.google.android.c2dm.intent.RECEIVE。registration和接受消息都是通过Intents实现的。
REGISTRATION的主要作用是允许程序接收registration ID。这个Intent可以在任何时候发送。Google可能定期刷新receiver ID。一个程序收到Intent包含registration_id
这个参数,必须确保第三方程序服务器收到registration ID。可以通过保存registration ID并发送到服务器来实现。如果网络断了或者有错误,程序应该尝试重新发送registration ID当网络连接上的时候。程序应该追踪registration的状态并且尝试重新注册当处理没完成的时候。
当注册没有完成的时候,REGISTRATION通常产生错误。如果发生了错误,程序应该稍后重试。当程序解除注册的时候,会发送包含unregisteered作为参数的REGISTRATION Intent。
下面是REGISTRATION Intent可能的error codes:
Error Code | Description |
SERVICE_NOT_AVAILABLE | 手机不能读取响应或者有500/503错误.程序应该使用指数退避然后重试。 |
ACCOUNT_MISSING | 手机上没有登录google账户. 程序应该要求用户打开账户控制并增加一个账户。 |
AUTHENTICATION_FAILED | 错误的密码,程序应该让用户输入正确的密码,并在稍后手动重试。 |
TOO_MANY_REGISTRATIONS | 用户有太多的注册程序. 程序应该告诉用户卸载一部分注册程序。然后手动重试。 |
INVALID_SENDER | Sender account不能被识别。 |
PHONE_REGISTRATION_ERROR | 这个手机现在不支持C2DM。 |
程序收到一个REGISTRATION Intent 广播当第三方程序服务器尝试发送消息给它的时候。但是registration IDs不存在的原因有以下几种:
1. 程序第一次运行,还没有registration ID。
2. 程序解除注册。
3. C2DM服务器端定时刷新了registration IDs。
程序必须准备好去应对这几种情况,比如:
public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
//注册相关 handleRegistration(context, intent); } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
//消息相关 handleMessage(context, intent); } } private void handleRegistration(Context context, Intent intent) { String registration = intent.getStringExtra("registration_id"); if (intent.getStringExtra("error") != null) { // 注册失败。稍后重试 } else if (intent.getStringExtra("unregistered") != null) { // 解除注册成功。授权的服务器发送的新消息将会被拒绝。
} else if (registration != null) { // 发送 registration ID 到发送消息的第三方程序服务器。 //应该开启一个新的线程去发送 registration ID。到此,注册就完了。
} }
Handling Received Data
当C2DM服务器收到从第三方程序服务器发送过来的消息时,C2DM服务器从消息中提取出键值对然后以com.google.android.c2dm.intent.RECEIVE Intent作为载体,把键值对发送到程序。程序根据key从消息中提取数据并处理数据。
比如:
protected void onReceive(Context context, Intent intent) { String accountName = intent.getExtras().getString(Config.C2DM_ACCOUNT_EXTRA); String message = intent.getExtras().getString(Config.C2DM_MESSAGE_EXTRA); if (Config.C2DM_MESSAGE_SYNC.equals(message)) { if (accountName != null) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Messaging request received for account " + accountName); } ContentResolver.requestSync( new Account(accountName, SyncAdapter.GOOGLE_ACCOUNT_TYPE), JumpNoteContract.AUTHORITY, new Bundle()); } } }