RN 加载流程与通讯机制 分析
-
求知
- RN (js代码) 加载和运行环境
- 在开启远程调试模式下
- Native如何把命令 发送到JS中
- JS如何执行原生方法
-
源码查看和学习 总结
- 启动服务
- 启动服务
-
部分代码
Native #import <React/RCTView.h> @interface ABCDView : UIView @property (nonatomic, copy) RCTDirectEventBlock onAbcd; @end @implementation ABCDViewManager RCT_EXPORT_MODULE(); -(UIView*)view{ return [[ABCDView alloc] init]; } RCT_EXPORT_VIEW_PROPERTY(onAbcd, RCTDirectEventBlock); RCT_REMAP_METHOD(domeMethod2, A:(NSString*)name B:(CGSize)size){} @end RN <SourceView style={{ height: 100, width: 100, backgroundColor: "red", }} onAbcd={(event) => { }}> </SourceView>
Native: @interface TestToolManager : RCTEventEmitter @end @implementation TestToolManager RCT_EXPORT_MODULE() -(NSArray<NSString *> *)supportedEvents{ return @[@"TestEvent"]; } -(void)xxx{ [self sendEventWithName:@"TestEvent" body:@[@"111",@"ZZH"]]; } @end RN: const ToolEmitter = new NativeEventEmitter(TestToolManager); ToolEmitter.addListener('TestEvent',(body) => { })
-
通讯过程
-
Native ===> JS 原生调用js方法
-
[1] Native (iOS)
-
[2] React (JS)
-
RN(js) 接受 Native 通讯 1 (开启远程调试)
UI管理类 RCTViewManager
-
[1] 文件:http://localhost:8081/debugger-ui/
function connectToDebuggerProxy() { const ws = new WebSocket('ws://' + window.location.host + '/debugger-proxy?role=debugger&name=Chrome'); let worker; function createJSRuntime() { worker = new Worker('debuggerWorker.js'); worker.onmessage = function(message) { ws.send(JSON.stringify(message.data)); }; } ws.onmessage = async function(message) { //接受服务器数据 const object = JSON.parse(message.data); if (object.method === 'prepareJSRuntime') { } else if (object.method === '$disconnected') { } else if (object.method === 'executeApplicationScript') { } else { /** { arguments: "RCTEventEmitter" "receiveEvent" [ 2, "topAbcd", {ASAS: 2121, target: 2} 参数 + 组件reactTag ] id: 11511 method: "callFunctionReturnFlushedQueue" */} /// 处理任务 worker.postMessage(object); } }; }
-
[2] http://localhost:8081/debugger-ui/debuggerWorker.js
//执行完成回调原生的方法 var sendReply = function(result, error) { postMessage({replyID: object.id, result: result, error: error}); }; //执行 /** __fbBatchedBridge == MessageQueue object.method == "callFunctionReturnFlushedQueue" object.arguments = [ "RCTEventEmitter" "receiveEvent" [2, "topAbcd", {ASAS: 2121, target: 2}] ] __fbBatchedBridge 见 react-native/Libraries/BatchedBridge/BatchedBridge.js 处理队列 接受消息 处理 并派发处理 **/ returnValue = __fbBatchedBridge[object.method].apply(null, object.arguments);
-
[3] node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js
callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) { this.__callFunction(module, method, args); return this.flushedQueue(); } __callFunction(module: string, method: string, args: any[]): any { /** args=[2,"topAbcd",{ASAS: 2121, target: 2}] moduleMethods ==> RCTEventEmitter 模块 */ const result = moduleMethods[method].apply(moduleMethods, args); return result; }
-
[4] node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js
/** * rootNodeID 对应<ABCDView onAbcd={xx}> 对象ID * topLevelType = topAbcd 对应 onAbcd 方法 * nativeEventParam {ASAS: 2121,target: 2} 参数 + 组件reactTag(target) */ function receiveEvent(rootNodeID, topLevelType, nativeEventParam) { _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam); } /** 一系列的处理 */ function executeDispatch(event, simulated, listener, inst) { var type = event.type || "unknown-event"; event.currentTarget = getNodeFromInstance(inst); ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( type, // listener, // onAbcd 需要调用的方法 undefined, event // 事件包含 (对应方法,组件实例,参数 。。。) ); event.currentTarget = null; } ..... var invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) { var funcArgs = Array.prototype.slice.call(arguments, 3); try { /** * func 为对应组件实例下的对应方法 ABCDView.onAbcd * funcArgs 为参数 */ func.apply(context, funcArgs); } catch (error) { } };
-
[5] 你xxx.js
<SourceView onAbcd={(params)=>{}}/>
-
-
RN 接受 Native 通讯 2 (开启远程调试)
功能类 通知 (继承 RCTEventEmitter)
-
[3] node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js
__callFunction(module: string, method: string, args: any[]): any { /** method ==> 'emit' moduleMethods ==> RCTDeviceEventEmitter 模块 args==>[ "TestEvent", [111,ZZH] ] */ const result = moduleMethods[method].apply(moduleMethods, args); return result; }
-
[4] RCTDeviceEventEmitter
emit =>派发通知
-
[5] 接受通知
ToolEmitter.addListener('TestEvent',(body) => { })
-
-
-
JS ===> Native JS调用Native方法
-
JS
-
[1] 执行导出方法
NativeModules.ABCDViewManager.domeMethod2("朱子豪", [111, 222])
-
[2] node_modules/react-native/Libraries/BatchedBridge/NativeModules.js
fn = function(...args: Array<any>) { BatchedBridge.enqueueNativeCall(moduleID, methodID, args, onFail, onSuccess); };
-
[3] node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js
enqueueNativeCall( moduleID: number, methodID: number, params: any[], onFail: ?Function, onSucc: ?Function, ) { this._callID++; this._queue[MODULE_IDS].push(moduleID); this._queue[METHOD_IDS].push(methodID); this._queue[PARAMS].push(params); /* 这里仅仅将 一次调用 存进 _queue队列中 那什么时候触发了 */ }
-
[4] 回调时机
MessageQueue 当我们进行一次JS=>Native 并不会立刻触发 仅仅存入_queue队列中 http://localhost:8081/debugger-ui/debuggerWorker.js return function(message) { var object = message.data; var sendReply = function(result, error) { postMessage({replyID: object.id, result: result, error: error}); }; returnValue = __fbBatchedBridge[object.method].apply(null, object.arguments); sendReply(JSON.stringify(returnValue), error); }; 有这么一段简化的代码 returnValue 最开始我以为是一次na=>js 的结果对调 后来发现 一次na=>js 调用后。会直接调用sendReply方法 查看代码后发现 returnValue = [[], [], [], 0]; 不仅包含一次返回结果 returnValue[3] returnValue[0] [1] [2] 分别值得什么意思了 ----------------------------------------------- node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) { this.__guard(() => { this.__callFunction(module, method, args); }); return this.flushedQueue(); } flushedQueue() { this.__guard(() => { this.__callImmediates(); }); const queue = this._queue; this._queue = [[], [], [], this._callID]; return queue[0].length ? queue : null; } callFunctionReturnFlushedQueue 为一次na=>js 执行方法 会返回this.flushedQueue() 执行方法结果 flushedQueue() 返回_queue 并会清空_queue数组 总结 js(RN) ==> 原生(iOS) 1:把一次命令存入消息对象中 _queue 2:当原生调用RN时 1:执行这次(native==>js)命令 2:会将消息队列中的命令 通过WS 发送到原生 3:执行原生对应方法 疑问 如果某段时间内 _queue有命令,但是无(native=>js)命令。那不是_queue队列消息不是无法触发 有兴趣可以查看 ios / RCTTiming.m ==> JSTimers 会保持一定的回调周期
-
[5] 发送事件给原生
http://localhost:8081/debugger-ui/debuggerWorker.js 57 行 http://localhost:8081/debugger-ui/ 122 行 Ws 发送 /** "{"replyID":10483,"result":"[[43,25,5,1],[19,1,3,1],[[],[72],[8,3,{\"type\":\"frames\",\"frames\":[0,0.008888888888888889,0.035555555555555556,0.08000000000000002,0.14222222222222222,0.22222222222222227,0.3200000000000001,0.4355555555555557,0.5644444444444444,0.6799999999999999,0.7777777777777777,0.8577777777777778,0.9199999999999999,0.9644444444444443,0.991111111111111,1,1],\"toValue\":1,\"iterations\":1},4907],[\"朱子豪\",[111,222]]],2451]"}" */ ws.send(JSON.stringify(message.data));
-
-
Native 接受JS消息
-
[1] node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.m
message ==js 发送的对像 "{"replyID":10483,"result":"[[43,25,5,1],[19,1,3,1],[[],[72],[8,3,{\"type\":\"frames\",\"frames\":[0,0.008888888888888889,0.035555555555555556,0.08000000000000002,0.14222222222222222,0.22222222222222227,0.3200000000000001,0.4355555555555557,0.5644444444444444,0.6799999999999999,0.7777777777777777,0.8577777777777778,0.9199999999999999,0.9644444444444443,0.991111111111111,1,1],\"toValue\":1,\"iterations\":1},4907],[\"朱子豪\",[111,222]]],2451]"}" - (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message { NSError *error = nil; NSDictionary<NSString *, id> *reply = RCTJSONParse(message, &error); NSNumber *messageID = reply[@"replyID"]; //处理消息的回调方法 RCTWSMessageCallback callback = _callbacks[messageID]; if (callback) { callback(error, reply); [_callbacks removeObjectForKey:messageID]; } }
-
[2] node_modules/react-native/React/CxxBridge/RCTObjcExecutor.mm
54行 m_jsThread->runOnQueue([this, json]{ m_delegate->callNativeModules(*this, convertIdToFollyDynamic(json), true); });
-
[3] node_modules/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp
[ [43,25,5,1] [19,1,3,1], [[],[92],[xx],["朱子豪",[11,22]]] ] ==> [43,19,[]] [19 1 [92]] [5,3,[xx]] [1,1,["朱子豪",[11,22]]] ===> [1,1,["朱子豪",[11,22]]] =====> [0]为模块标识 [1]为方法标识 [2]为方法调用参数 47行 for (auto& call : parseMethodCalls(std::move(calls))) { m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId); }
-
[4] node_modules/react-native/ReactCommon/cxxreact/ModuleRegistry.cpp
void ModuleRegistry::callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) { if (moduleId >= modules_.size()) { throw std::runtime_error( folly::to<std::string>("moduleId ", moduleId, " out of range [0..", modules_.size(), ")")); } //[1,1,["朱子豪",[11,22]]] =====> [0]为模块标识 [1]为方法标识 [2]为方法调用参数 // moduleId 查找RCTNativeModule模块 mthodID 方法标识 params参数 modules_[moduleId]->invoke(methodId, std::move(params), callId); }
-
[5] node_modules/react-native/React/CxxModule/RCTNativeModule.mm
71 行 invokeInner(weakBridge, weakModuleData, methodId, std::move(params)); 86 行 static MethodCallResult invokeInner(RCTBridge *bridge, RCTModuleData *moduleData, unsigned int methodId, const folly::dynamic ¶ms) { //找到对应方法 method == RCTModuleMethod 1: 包含对应方法domeMethod2 的 NSInvocation对象 2: 包含你声明的管理对应应用ABCDViewManager 信息 3: 方法选择器 id<RCTBridgeMethod> method = moduleData.methods[methodId]; NSArray *objcParams = convertFollyDynamicToId(params); @try { id result = [method invokeWithBridge:bridge module:moduleData.instance arguments:objcParams]; return convertIdToFollyDynamic(result); } return folly::none; }
-
[6] node_modules/react-native/React/Base/RCTModuleMethod.mm
544行 执行 NSInvocation [_invocation invokeWithTarget:module];
-
[7] ABCDViewManager.m
RCT_REMAP_METHOD(domeMethod2, A:(NSString*)name B:(CGSize)size){ //调用方法 }
-
-
-