[原创]通过代码及流程图说明Google在Android上的Push机制的实现
声明: 如果您要转载, 请事先征得本人的同意后方可并且请您附上原文链接. 本人保留一切权利. 多谢!
Google从FroYo版本后引入了C2DM (Cloud to Device Messaging)框架:
http://code.google.com/android/c2dm/index.html
Android Cloud to Device Messaging (C2DM) is a service that helps developers send data from servers to their applications on Android devices. The service provides a simple, lightweight mechanism that servers can use to tell mobile applications to contact the server directly, to fetch updated application or user data. The C2DM service handles all aspects of queueing of messages and delivery to the target application running on the target device.
这个C2DM框架实际上就是一种Push机制, 当服务器端有changes(creates/updates/deletes)时, 服务器端立刻会通过Push机制通知客户端, 然后客户端会通过Sync机制从服务器端获取server-side diffs, 同时也会把client-side diffs发送给服务器端. 通过Push机制, 用户可以及时获得服务器端的改动, 因此有更好的用户体验.
注:
1. Push机制的实现有多种, 比如通过SMS, 以及现在介绍的C2DM.
2. Android上的Sync机制这里不做过多介绍. 实际上对应下面介绍的内容Google是把Push机制和Sync机制一起使用的.
那么, C2DM的实现原理是什么呢? XMPP!!! 和Android GTalk Client的协议实现一样, 都是XMPP, 并且用的都是同一个XMPP框架, XMPP Stack的实现用的是开源的Smack. 其实, 在FroYo之前, Google的Push机制直接就是在XMPP上面, 只不过在引入了C2DM后, 变成Push机制在C2DM上面, 然后C2DM在XMPP上面了.
下面, 通过介绍Android上Google Contacts/Calendar如何利用C2DM实现Push机制来进行说明. 说的顺序可能比较乱, 大家慢慢理解, 并且其中的code只是用来帮助大家来进行理解.
1. 在AndroidManifest.xml上declare了一个broadcast receiver和一个对应的service用来接收C2DM发送的通知.
<receiver android:name=".subscribedfeeds.SubscribedFeedsBroadcastReceiver"><intent-filter><action android:name="com.google.android.c2dm.intent.RECEIVE" /><category android:name="com.google.android.gsf.subscribedfeeds" /></intent-filter></receiver><service android:name=".subscribedfeeds.SubscribedFeedsIntentService" />
public class SubscribedFeedsBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { intent.setClass(context, SubscribedFeedsIntentService.class); context.startService(intent); } }}public class DataMessageManager implements PacketListener{......}PacketTypeFilter filter = new PacketTypeFilter(DataMessage.class);connection.addPacketListener(this, localPacketTypeFilter);
public class DataMessage extends Message{ ...... private ArrayList<AppData> mAppDataList; private String mCategory; private boolean mFromTrustedServer = 0; private String mPermission; private String mToken; ......}public String getExtensionsXML() { StringBuilder buf = new StringBuilder() buf.append("<data").append(" xmlns="")append("google:mobile:data").append("""); if (getCategory() != null) buf.append(" category="").append(getCategory()).append("""); if (getToken() != null) buf.append(" token="").append(getToken()).append("""); if (getPermission() != null) buf.append(" permission="").append(getPermission()).append("""); if (this.mFromTrustedServer) buf.append(" from-trusted-server="true""); buf.append(">"); int size = getAppDataSize(); for (int i = 0; i < size; i++) { AppData appData = (AppData)this.mAppDataList.get(i); buf.append("<app-data key="").append(appData.getKey()).append("" value="") .append(appData.getValue()).append("" />"); } buf.append("</data>"; return buf.toString();}<message id="zUwxRf4-9" persistent_id="0:1295969265963580%e7a71353002ea1e0" from="google.com" type="headline"><data xmlns="google:mobile:data" category="GSYNC_TICKLE" token="cl_6" from-trusted-server="true"><app-data key="account" value="zhangsan.android@gmail.com" /></data></message>
String category= dataMessage.getCategory();if ("GSYNC_TICKLE".equals(str2)) category = "com.google.android.gsf.subscribedfeeds";Intent intent = new Intent("com.google.android.c2dm.intent.RECEIVE");intent.addCategory(category);......context.sendBroadcast(intent);ContentResolver.requestSync(......);
