react vr中文網:www.vr-react.com
react vr qq群:481244084
示例源碼 github:https://github.com/LiuC520/ReactVR/
上一篇講解了react vr原理:
開發者頭條:react vr 原理解析
開發者頭條:React VR 視頻源碼解析
開發者頭條:react vr 消息傳遞原理解析
里面講到了搬運工-worker;
這個worker只是在React VR的RN上下文中出現過一次,也就是實例化一次。
這里就講解下這個搬運工到底是干嘛的,他是如何傳遞數據解析數據的?
一、什么是worker?
它是獨立于JS主線程的一個東西,但是受限于主線程,不能訪問dom對象,就相當于android的子線程的概念,在這個worker內部可以執行代碼,可以加載代碼,執行耗時的操作,執行完畢后可以發送出來結果,我稱它為搬運工,是因為它既能發送消息,也能接收消息,他的方法就下面四個:
onerror:執行出錯的事件監聽,每當類型為error的ErrorEvent 從 worker 中冒泡出來時就會執行該函數;
onmessage:執行成功的事件監聽,每當擁有message 屬性的MessageEvent從 worker 中冒泡出來時就會執行該函數。事件的data屬性存有消息內容;
postMessage:發送消息;
terminate:工人收工回家,終止worker;
具體的概念和示例大家可以查看MDN:使用 WebWorkers。
二、worker創建流程
2.1、首先在VRInstace里面創建根組件,在createRootView里面實例化RN上下文(ReactNativeContext),然后在上下文里面實例化這個搬運工,創建一個新的搬運工,然后給搬運工一段接受消息的代碼(onmessage);
? ? 因為每一個view(不管是react vr的視圖還是模型,還是你自定義的視圖)內部都有一個UIManager,而UIManager里面就有RN上下文對象,所以每一個視圖都會接收或者發送消息的,所以搬運工收發消息,在視圖里面都能夠處理,這個邏輯不知道大家有沒有搞懂,容易繞暈。
? ? 上面給搬運工的消息是啥呢,是把搬運工要執行的代碼放到Blob對象里面,然后再創建一個DOMString(DOMString是一個UTF-16字符串。由于JavaScript已經使用了這樣的字符串,所以DOMString 直接映射到 一個String)。把這個DOMString傳遞給worker里面。
這個domstring可以直接作為圖片的src,它是由URL.createObjectURL()靜態方法創建的,它的參數是一個file或者blob對象,file就是文件,blob是一個不可變的, 原始數據的類似文件對象。Blob表示的數據不一定是一個JavaScript原生格式。File接口基于Blob,繼承 blob功能并將其擴展為支持用戶系統上的文件。
把搬運工要執行的字符串放到Blob里面后,就會形成一個新的Blob對象如:
然后創建一個靜態的domstring,類似于:
blob:http://localhost:8081/fbf49efb-b427-43ce-a13c-2221a6fdfa50
也就是把上面的字符串地址(domstring)傳遞給搬運工。
2.2、創建完搬運工,然后調用RN上下文的初始化,初始化里面做了兩件事:
一個是讓工人發送第一個模塊配置指令(moduleConfig)然后把各個模塊裝進去,以字符創的形式發送出去。截止1.4.0這個版本,有以下17個模塊再里面(UIManager、AndroidConstants、AsyncLocalStorage、ControllerInfo、History、Networking、LinkingManager、Location、Timing、VideoModule、AudioModule、WebSocketModule、ReactVRConstants、RCTExceptionsManager、RCTSourceCode、ExternalAssets、GlyphTextures),具體的每一個模塊代表啥意思,請查看react vr原理解析:http://www.lxweimin.com/p/0d65456909d4的第六章的6.1介紹。
第二個是讓工人發送一條bundle指令,把bundle的url以字符串的形式發送出去。
2.3、上面把模塊和bundle的url發送出去,誰來接收這些消息呢---搬運工自己接收。
具體的代碼大家可以在react-vr-web ---> js ---> createRootView.js里面的BRIDGE_CODE,也可以在 react-vr-web ---> js ---> RNBridge里面看到。
如下圖所示:
來解釋下這段代碼是啥意思:
2.3.1、首先解析獲取的數據,然后先執行上面的模塊配置指令,給一個全局的__fbBatchedBridgeConfig就是包含各種注冊模塊的一個對象,然后把狀態改成模塊配置‘moduleConfig’;
2.3.2、然后到bundle指令了,如果指令是bundle,而且狀態是模塊配置,就導入bundle,把狀態改成‘bundle’,但是如果導入失敗,就用XMLHttpRequest的GET方法獲取地址,然后給個警告出來;
2.3.3、然后就是重點了,如果狀態是bundle,也就是bundle的東東全部導進來了,說明react 和client已經打通了建立消息的通道了,這時間就可以解析各種發過來的指令了,其實也就是給過來的指令內容分類,不同的指令給分配不停的處理方法,然后包裝下再次發送出去,發出去以后,在RN的上下文里面接收到這個包裝后的指令內容,再把這些內容放到消息隊列里面去,然后在RN上下文的frame方法里面逐一處理這些結果,下面再講;現將下解析的指令有3種:
一個exec:拿到消息里面的module(模塊)、function(方法)、args(參數),調用__fbBatchedBridge的callFunctionReturnFlushedQueue的apply方法包裝下,然后再postMessage發送出去;
一個是invoke:拿到消息里面的id、args(參數),調用__fbBatchedBridge的invokeCallbackAndReturnFlushedQueue的apply方法包裝下,然后再postMessage發送出去;
一個是flush:直接調用__fbBatchedBridge的flushedQueue的apply方法包裝下,然后再postMessage發送出去;
2.4、上面講到了__fbBatchedBridge這個東西,他是React Native的BatchedBridge橋里面定義的一個全局的對象,對象的value就是實例化一個React Native的消息隊列MessageQueue,可以再其他地方調用消息隊列里面的方法。
? ? __fbBatchedBridge定義的路徑在RN源碼,在你的項目下面的node_modules ---> react-native ---> Libraries ---> BatchedBridge.js 里面,上面2.3.3里面重新包裝的__fbBatchedBridge的方法就在 node_modules ---> react-native ---> Libraries ---> MessageQueue.js里面。
? ? 2.4.1、callFunctionReturnFlushedQueue:
? ? ? 這個方法先調用__callFunction來調用模塊的方法,把返回值返回,然后調用調用flushedQueue
? ? 2.4.2、invokeCallbackAndReturnFlushedQueue
? ? 這個方法和上面的區別是會把那個返回值返回出去,因為是回調函數
? ? 2.4.3、flushedQueue:返回消息隊列,如果沒有,就返回null,清空下消息隊列。
2.5、在RN的上下文里面接收到上面這個包裝后的指令內容,再把這些內容放到消息隊列里面去,然后在RN上下文的frame方法里面逐一處理這些結果:
2.5.1、frame的代碼如下:
2.5.2、舉個例子:
獲取到的messages如下:
messages有三個成員,第一個是模塊的index,第二個是調用第幾個模塊的第幾個方法,第三個參數是前面方法的參數
2.5.3、再看下模塊,也就是最開始注冊的17個模塊:
對比frame的最后循環模塊的index代碼:
for ( let i = 0 ; i < moduleindex.length ; i++ ) {
? ? this.modules[moduleIndex[ i ] ]._functionMap[funcIndex[ i ]].apply(
? ? ? ? this.modules[moduleIndex [ i ]],
? ? ? ? params[ i ]
? ? ? ?);
}
moduleIndex的長度為9,模塊就是調用第一個模塊也就是moduleIndex[ i ]都為0,也就是UIManager模塊;
2.5.4、然后調用UIManager的_functionMap方法,方法有24個:
但是看方法的index也就調用了第一和第二兩個方法,第一個方法是創建視圖,第二個添加子視圖;
三、自行定義發送事件功能
舉個例子,我們在react vr 業務代碼也就是bundle里面發送一個消息,可以再client里面接收這個消息,然后處理,同樣也可以反過來在client里面發送消息,在react那邊接收處理消息,比如在client一側監聽鼠標的移動,然后把鼠標的坐標傳到react 那一側,然后移動react vr里面的物體或者翻轉里面的物體:等等
具體可以看下我前面簡單寫的一篇消息傳遞的文章:react vr消息傳遞 http://www.lxweimin.com/p/cdd2f30118b9?,
具體的代碼可以查看示例:http://www.vr-react.com/example/threejs/webgl_camera_cinematic/,就監聽了鼠標的滾輪,而且還從index.vr.js里面發送消息,在client里面接收消息,并處理原生的東西:
vr.rootView.context.worker.addEventListener('message',onVRMessage);//接受消息的在react vr那邊直接? postMessage({ type: "sceneLoadStart"})
這個示例是用react vr重寫threejs的實例,源碼在github上:https://github.com/LiuC520/ReactVR/tree/master/examples/threejs/webgl_camera_cinematic