Android中WAP PUSH的实现分析

来源:互联网 发布:罗素 作品 知乎 编辑:程序博客网 时间:2024/05/17 09:12

1 WAP PUSH 流程简介 
WAP Push分为两种:SI(Service Initiate) SL(Service Load)。都是服务器端向客户端推送消息的一种方式。 
先看SI: 
图发不上来’ 
1.1 
服务器通过网关采用OTA协议把信息发送到手机,手机存储解析并存储信息,然后提示给用户。 

SL流程如下: 
图发不上来’ 
1.2 
在接收到SL消息后,同样也会存储并提示用户(视情况具体对待),区别在于客户端会主动调用浏览器打开SL中附带的连接。 

2 WAP PUSH消息体剖析 
下面我们以SL为例,来看看WAP PUSH的消息。 
一个SLWAP PUSH消息主体大致如下: 
<!-- 
Service Loading (SL) Document Type Definition. 
SL is an XML language.  Typical usage: 
   <?xml version="1.0"?> 
   <!DOCTYPE sl PUBLIC "-//WAPFORUM//DTD SL 1.0//EN" 
          "http://www.wapforum.org/DTD/sl.dtd"> 
   <sl> 
   href %URI; #REQUIRED 
  action (execute-low|execute-high|cache) "execute-low" 
   </sl> 
大家都看到了,其实内容很少,包括一个连接和一个action控制执行的优先级。 
由于考虑到节约网络数据的传输,往往都是将这些文本转化为WBXML格式再进行传输。WBXML是一种压缩的二进制表示方式,有严格的定义。对于标签和属性,定义如下表: 
Tag Name Token 
sl 5 

Attribute Name Attribute Value Prefix Token 
action execute-low 5 
action execute-high 6 
action cache 7 
href 8 
href http:// 9 
href http://www. A 
href https:// B 
href https://www. C 

Attribute Value Token 
.com/ 85 
.edu/ 86 
.net/ 87 
.org/ 88 

文本中的对应TagAttribute value会被Token代替,以减少文本大小。我们看下面一段内容: 
<?xml version="1.0"?> 
     <!DOCTYPE sl PUBLIC "-//WAPFORUM//DTD SL 1.0//EN" 
                  "http://www.wapforum.org/DTD/sl.dtd"> 
<sl href="http://www.xyz.com/ppaid/123/abc.wml"></sl> 
总共有159个字节。 
我们使用WBXML格式来表示,如下表对应关系: 
Token Stream Description 
02 Version number - WBXML version 1.2 
06 SL 1.0 Public Identifier 
6A Charset=UTF-8 (MIBEnum 106) 
00 String table length 
05 sl, with attributes 
0A Token for "href="http://www." 
03 Inline string follows 
xyz, 00 String 
85 Token for ".com/" 
03 Inline string follows 
ppaid/123/abc.wml, 00 String 
01 END (of sl attribute list) 
这样一来,就是: 
02  06  6A  00  05  0A  03  'x' 'y' 'z' 00  85  03  'p' 'p' 'a' 
'i' 'd' '/' '1' '2' '3' '/' 'a' 'b' 'c' '.' 'w' 'm' 'l' 00  01  
总共才需要32个字节大大减少了数据量。 
以上的只是消息主体内容,在传输过来的时候,还需要加上一些附加的信息,如 
mimeType: 表示是SI还是SL, application/vnd.wap.sic或者 application/vnd.wap.slc (text/vnd.wap.sl). 
transactionId: 用于分段发送大数据量的消息, 这些消息具有相同的trasactionId. 
pduType: 未识别 
header: 附加信息,用于标示特定的业务。 
3 Android中解析WAP PUSH 
按照以上所述,网络传输过来的是WBXML数据,通常我们需要按照Token表对应的进行解析,才可以得到XML格式的正文,从而获取hrefaction。 庆幸的是Android中在framework层的WapPushOverSms.java中已经完成了对消息的部分解析,把mimeType, trasactionId, pduType, header 和 data分离出来,放在intent的传递参数中了。看以下代码: 
private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType, 
                                       int headerStartIndex, int headerLength) { 
        byte[] header = new byte[headerLength]; 
        System.arraycopy(pdu, headerStartIndex, header, 0, header.length); 

        Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); 
        intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO); 
        intent.putExtra("transactionId", transactionId); 
        intent.putExtra("pduType", pduType); 
        intent.putExtra("header", header); 
        intent.putExtra("data", pdu); 

        mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); 
    } 

我们在应用层只需要添加对WAP_PUSH_RECEIVED_ACTION的监听,便可获取到这个WAP PUSH。 下面以接收中国移动DCD业务的WAP PUSH为例,看看是如何实现的。 
需要写一个DcdWapPushReceiver.java: 
public class DcdWapPushReceiver extends BroadcastReceiver { 

    private static final String LOGTAG = "DcdPushReceiver"; 
    
    public static final String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc"; 
    public static final String APPLICATION_DCD_ID = "application/x-oma-DCD:DCD.ua"; 
    
    public void onReceive(Context context, Intent intent) { 
        //only deal with SL WAP PUSH 
        if (intent.getAction().equals(WAP_PUSH_RECEIVED_ACTION) 
                && CONTENT_MIME_TYPE_B_PUSH_SL.equals(intent.getType())) { 
                // Start a new AsyncTask to process the data. 
                if (APPLICATION_DCD_ID.equals(getAppIdFromIntent(intent))) { 
                    new ReceivePushTask(context).execute(intent); 
                } 
        } 
        
    } 
    
    /* 
     * get application id from intent header. 
     * <==header sample==> 
     * B0 B4 87 AF application/x-oma-DCD:DCD.ua 00 Encoding-Version 00 
     */ 
    public static String getAppIdFromIntent(Intent intent) { 
        byte[] header = intent.getByteArrayExtra("header"); 
        if (header == null) { 
            return null; 
        } 
        
        String str = new String(header); 
        int start = str.indexOf("application/"); 
        if (start > 0) { 
            //application id end with 00. 
            int end = str.indexOf(0x00); 
            if (end > 0) { 
                str = str.substring(start, end); 
                return str; 
            } 
        } 
        return null; 
    } 
private class ReceivePushTask extends AsyncTask<Intent, Void, Void> { 
       
        private Context mContext; 
        
        public ReceivePushTask(Context context) { 
            mContext = context; 
        } 
        
        protected Void doInBackground(Intent... intents) 
        { 
            Intent intent = intents[0]; 
            // step1. obtain wbxml data from intent. 
            byte[] pushData = intent.getByteArrayExtra("data"); 
            
            String wbxmlData = ""; 
            for (byte by: pushData) { 
                wbxmlData = wbxmlData + by + " "; 
            } 
            Debug.print("wap push data = "+wbxmlData); 
            
                        
            // step2. pass the data to WapPushParser and get the parsing result. 
            DcdWapPushParser parser = new DcdWapPushParser(pushData); 
            DcdWapPushMsg pushMsg = null; 

            if (CONTENT_MIME_TYPE_B_PUSH_SL.equals(intent.getType())) { 
                pushMsg = parser.parse(DcdWapPushMsg.WAP_PUSH_TYPE_SL); 
            } 
                        
            if (null == pushMsg) { 
                Debug.error("Invalid WAP PUSH data"); 
                return null; 
            } 
            
            //get href 
            String href = pushMsg.getAttributeValueString(DcdWapPushMsg.WAP_PUSH_PROJECTION_HREF); 
            Debug.print("href = " + href); 
            Intent i = new Intent(mContext, DcdCmService.class); 
            
            //step3. sync invoked by wap push, so set sync type to SVR 
            //i.putExtra("syncType", DcdCmService.SYNC_TYPE_SVR); 
            //i.putExtra("href", href); 
            Bundle bundle = new Bundle(); 
            bundle.putBoolean(DcdCmService.NOTIFICATION_WAPPUSH, true); 
            bundle.putString("syncType", DcdRequest.REQUEST_TYPE_SVR); 
            bundle.putString("href", href); 
            i.putExtras(bundle);            
            //start sync service 
            mContext.startService(i); 
            
            return null; 
        } 
    } 

AndroidManifest.xml中注册这个接收器: 
<receiver android:name=".contentmanager.DcdWapPushReceiver"> 
            <intent-filter> 
                <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" /> 
                <data android:mimeType="application/vnd.wap.slc" /> 
            </intent-filter> 
        </receiver>  

摘自:http://seya.iteye.com/blog/903137 

      见压缩包源码:AndroidWAP PUSH的实现分析.rar

原创粉丝点击