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註冊
對於註冊,也分為本地註冊和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的呼叫是由Native開始,通過WXBridge這個Js與Native互動的橋樑;
- 在WXModuleManager會呼叫findModule,上面本地註冊的時候有提到(是全域性的話則直接反射建立),此處未被建立的Module物件會被建立;
- 呼叫getMethodInvoker方法獲取呼叫方法包裝成的物件Invoker;
- 通過NativeInvokerHelper來呼叫,實際是是MethodInvoker反射呼叫的;
3.3 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相關的關鍵類
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乾貨!