深入Weex系列(四)之Module元件原始碼解析

頭條祁同偉發表於2017-10-24

1、前言

經過前面兩篇文章的實踐,我們學習了Weex的使用。本篇開始我們深入Weex的原始碼,一起探索Weex在安卓平臺上是如何構建一套JS的執行框架,那從Module開始說起吧。

本文從原始碼入手分析Module的註冊、呼叫、回撥等流程,並且分析一個Weex自帶Module的實現。

2、初識Module

2.1 Module的定位

《Android 擴充套件》中我們可以看到Module的定位:

Module 擴充套件非UI的特定功能,例如 sendHttp、openURL 等。

也就是說是非UI型別的功能提供,在本地註冊供Js執行時呼叫,有了Module,我們就可以自己擴充套件一些Weex沒有提供的能力給Js,讓Js更加強大!

2.2 Module的使用

還記不記得在上兩篇文章中我們就用到了Module來實現網路請求的能力,宣告+呼叫即完成了網路請求的呼叫;

    var stream = weex.requireModule('stream') // 宣告
    stream.fetch // 請求複製程式碼

3、Module原始碼分析

3.1 Module註冊

Module註冊時序圖
Module註冊時序圖

對於註冊,也分為本地註冊和Js註冊

  • 本地註冊:
    • 如果這個Module是全域性的,則直接反射建立Module物件,然後儲存在sGlobalModuleMap中;
    • Native註冊Module的過程很簡單,在sModuleFactoryMap中儲存了Module的Name及對應的ModuleFactory;
  • Js註冊:
    • WXBridge是Native與Js互動的橋樑,執行的是JNI層的Java_com_taobao_weex_bridge_WXBridge_execJS方法;
    • Js的註冊最終呼叫到了WXBridge的execJS方法,型別為METHOD_REGISTER_MODULES;

備註:註冊的意義在於Js與Native定義了一個協議,一個對應關係;呼叫的時候能找到Module。

3.2 Module呼叫

Module呼叫時序圖
Module呼叫時序圖

呼叫分析:

  • Module的呼叫是由Native開始,通過WXBridge這個Js與Native互動的橋樑;
  • 在WXModuleManager會呼叫findModule,上面本地註冊的時候有提到(是全域性的話則直接反射建立),此處未被建立的Module物件會被建立;
  • 呼叫getMethodInvoker方法獲取呼叫方法包裝成的物件Invoker;
  • 通過NativeInvokerHelper來呼叫,實際是是MethodInvoker反射呼叫的;

3.3 Native回撥Js

Native回撥Js時序圖
Native回撥Js時序圖

呼叫分析:

  • NativeInvokerHelper反射呼叫方法的時候回將JSCallback包裝成SimpleJSCallback物件;
  • Native呼叫完成需要回撥的時候呼叫到WXBridgeManager的callbackJavascript方法;
  • 隨後傳送訊息,任務執行就被切換到了WeexJSBridgeThread執行緒;
  • 最後也是呼叫的WXBridge回撥Js端;

3.4 Module原始碼總結

Module的原始碼並不複雜:

  • 在客戶端定義了Module來實現特定能力例如網路請求(就是一個提供方法的類);
  • 然後註冊到Native和Js(註冊過程就是讓雙方能找到對方);
  • 呼叫是從Js發起,通過WXBridge(互動橋樑,以後還會經常遇到)與Native互動的;
  • Native找到本地註冊的Class,通過反射呼叫Module方法;
  • 回撥也是通過WXBridge,傳遞資訊給Js;

3.5 Module相關的關鍵類

Module相關的關鍵類
Module相關的關鍵類

4、特定Module實現分析

分析了Module的註冊、呼叫、回撥等步驟,我們就來例項分析一個Weex中自帶的Module:WXStreamModule,我們網路請求的功能就是它實現的,在Weex程式碼中我們呼叫網路請求的是fetch方法,那麼我們在WXStreamModule方法中尋找fetch方法:


  @JSMethod(uiThread = false)
  public void fetch(String optionsStr, final JSCallback callback, JSCallback progressCallback){
            ......

            String method = optionsObj.getString("method");
            String url = optionsObj.getString("url");
            JSONObject headers = optionsObj.getJSONObject("headers");
            String body = optionsObj.getString("body");
            String type = optionsObj.getString("type");
            int timeout = optionsObj.getIntValue("timeout");

            sendRequest()

            ......
  }複製程式碼
  • 通過上述optionsObj中獲取各種資料的方式,我們可以推知在Weex程式碼中怎麼去宣告想要的引數,例如:Weex的Demo中沒有寫傳遞header的例子,我們檢視獲取Header的方式也知道需要將其封裝成一個JsonObject,從根源來發現解決問題的途徑是看原始碼的好處之一,原始碼面前了無祕密!

  • sendRequest是通過IWXHttpAdapter實現的,沒有設定的話會使用DefaultWXHttpAdapter;然後在一個FixedThreadPool裡通過HttpURLConnection執行的網路請求。

public class DefaultWXHttpAdapter implements IWXHttpAdapter {
    private ExecutorService mExecutorService = Executors.newFixedThreadPool(3);

    @Override
    public void sendRequest(final WXRequest request, final OnHttpListener listener) {
        HttpURLConnection connection = openConnection(request, listener);
    }

}複製程式碼

備註:通過細看WXStreamModule的原始碼我們不難發現最終網路請求的預設執行類DefaultWXHttpAdapter有一定缺陷,固定執行緒數的執行緒池實際上並不適合網路請求的場景,尤其在網路請求密集的場景下(整個模組、甚至應用都使用Weex來做,這種場景不會罕見)。因此如果我們要大面積使用Weex最好自己實現IWXHttpAdapter或者呼叫Native的方法。這是看原始碼的另一好處:從原始碼角度分析框架實現的利弊,定製更適合自己業務的實現。

自定義Module比較簡單,就不在本文細說了,可以參照官方文件或者自帶的Module的實現,也可以參考我寫的WeexList,裡面有自定義Module的使用。

5、Module總結

  • Module的定位是擴充套件非UI的特定功能;
  • Module原始碼的分析,包括:註冊、呼叫、回撥等;
  • 分析一個自帶的Module:WXStreamModule;
  • 通過本文也可以發現原始碼閱讀的好處:
    • 從根源來發現解決問題的途徑(Header的傳遞方式);
    • 原始碼角度分析框架實現的利弊,定製適合自己的實現(DefaultWXHttpAdapter裡的問題,自己定製);

歡迎持續關注Weex原始碼分析專案:Weex-Analysis-Project

歡迎關注微信公眾號:定期分享Java、Android乾貨!

歡迎關注
歡迎關注

相關文章