天天看點

手機探索者開發實錄—資料解包

手機探索者開發實錄—資料解包

資料打包比較容易,解包卻要困難得多。XML解析有DOM和SAX兩種方式,我比較喜歡SAX方式,一是比較簡單,不需要熟悉複雜的DOM API。二是可以邊解析邊處理。三是開源的expat簡單易用,而且支援UTF-8編碼。是以在手機探索者中,我們理所當然的采用SAX方式了。SAX是典型的builder模式,有了expat的幫助,我們要做的就是實作一些builder。

為了提高靈活性,也可以說是自找麻煩,我們允許同一個資料包中封裝多個請求/響應/事件,不同請求的參數不同,是以build方式也是不同的,如果把這些請求的build放在一起,代碼會顯得到複雜。怎麼辦呢,在這裡,我第一次發現狀态模式有了用武之地,不同的請求不就相當于不同的狀态嗎,每解析到一個請求,我們就把目前builder切換到相應的builder上,這就相當于狀态切換。可以建立一個查找表,根據名稱找到相應的builder,這樣邏輯上很清晰。

每個builder的實作很簡單,畢竟裡面的參數不多,不過要去寫幾十個builder很難說是件有趣的事。我喜歡單調的工作,因為我知道單調的工作總是有規律可循,找到這些規律就找到了解決問題的捷徑。做了簡單的分析之後,我發現參數的類型主要有三種:一是基本類型,像整數和字元串,二是結構,三是數組。其它所有類型都可以用這三種組合起來,于是就寫了三個builder分别處理這三種類型。然後隻要幾行代碼就可以為一個請求或者響應組合成一個builder。

Builder的接口定義如下,基本上是expat需要的接口:

  1. struct _MobileExplorerBuilder;
  2. typedef struct _MobileExplorerBuilder MobileExplorerBuilder;
  3. typedef MeRet (*MobileExplorerBuilderSetContextFunc)(MobileExplorerBuilder* thiz, void* ctx);
  4. typedef MeRet (*MobileExplorerBuilderOnStartFunc)(MobileExplorerBuilder* thiz, const char* name, con
  5. st char** atts);
  6. typedef MeRet (*MobileExplorerBuilderOnTextFunc)(MobileExplorerBuilder* thiz, const char* text, size
  7. _t length);
  8. typedef MeRet (*MobileExplorerBuilderOnEndFunc)(MobileExplorerBuilder* thiz, const char* name);
  9. typedef MeRet (*MobileExplorerBuilderDestroyFunc)(MobileExplorerBuilder* thiz);
  10. struct _MobileExplorerBuilder
  11. {
  12.     MobileExplorerBuilderSetContextFunc set_context;
  13.     MobileExplorerBuilderOnStartFunc on_start;
  14.     MobileExplorerBuilderOnTextFunc on_text;
  15.     MobileExplorerBuilderOnEndFunc on_end;
  16.     MobileExplorerBuilderDestroyFunc destroy;
  17.     char priv[0];
  18. };

雖然我們确定了用expat,它是開源的,跨平台的,也很好用。不過我還是不喜歡把自己綁定特定的函數庫上,我決定它寫一個parser函數,它隻是簡單的包裝expat,使用起來更簡單,也隔離了expat。

~~end~~