准备-JavaScriptCore
在开篇,我们先简单准备下JavaScriptCore的知识。这是整个Native和JS沟通的最底层的桥梁。
Classes
- JSContext 
 JS的执行上下文 
- JSManagedValue 
 主要用于防止Native导出对象时持有JSValue导致循环引用
- JSValue 
 JavaScript值的引用,转换JavaScript和Native之间的基本数据 
- JSVirtualMachine 
 提供JS执行环境 
- JSExport 
 导出Native到JavaScript
相对应的底层C实现
- JSBase
- JSContextRef
- JSObjectRef
- JSStringRef
- JSStringRefCF
- JSValueRef
ReactNative通信原理
从ReactNative的demo开始,入口我们只看到两个东西RCTRootView和RCTBridge。RCTRootView负责展示,RCTBridge则和ReactNative的通信有关。
入口
| 1 | - (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { | 
RCTCxxBridge初始化
跟着调用链会看到,bridge的初始化会走到RCTCxxBridge中的start1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36//RCTCxxBrige.mm
//只贴了关键代码
- (void)start {
  dispatch_group_t prepareBridge = dispatch_group_create();
  // 初始化native modules
  (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
  // 初始化底层Instance
  dispatch_group_enter(prepareBridge);
  [self ensureOnJavaScriptThread:^{
    [weakSelf _initializeBridge:executorFactory];
    dispatch_group_leave(prepareBridge);
  }];
  // 加载js代码
  dispatch_group_enter(prepareBridge);
  __block NSData *sourceCode;
  [self loadSource:^(NSError *error, RCTSource *source) {
    if (error) {
      [weakSelf handleError:error];
    }
    sourceCode = source.data;
    dispatch_group_leave(prepareBridge);
  } onProgress:^(RCTLoadingProgress *progressData) {
  }];
  // 执行js代码
  dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
    RCTCxxBridge *strongSelf = weakSelf;
    if (sourceCode && strongSelf.loading) {
      [strongSelf executeSourceCode:sourceCode sync:NO];
    }
  });
}
start方法里做了这么几件事:
- 初始化native modules
- 初始化Instance->底层负责通信
- 加载本地js
要想了解native modules的初始化,我们要先看下之前的准备工作。
native modules导出模块
通过RCT_EXPORT_MODULE将本地的模块导出供JS使用
| 1 | //RCTBridgeModule.h | 
最终导出的模块会被保存到RCTModuleClasses,使用时通过RCTGetModuleClasses()获取。RCTGetModuleClasses()就是在上面初始化native modules时用到的。1
2
3
4
5
6
7
8//RCTBridge.m
NSArray<Class> *RCTGetModuleClasses(void) {
  __block NSArray<Class> *result;
  dispatch_sync(RCTModuleClassesSyncQueue, ^{
    result = [RCTModuleClasses copy];
  });
  return result;
}
native modules导出方法
| 1 | //RCTBridgeModule.h | 
将方法导出,最终生成以下方法提供给外部调用。通过遍历这个类中所有以
“__rct_export__“开头的方法就可以获取属于这个类的所有导出方法。
| 1 | //RCTBridgeModule.h | 
生成RCTModuleData
初始化native modules的工作,其实就是根据之前导出的类和方法,生成对应的RCTModuleData对象。
| 1 | //RCTCxxBridge.mm | 
至此,我们完成了本地模块和方法的导出,并且生成了一组RCTModuleData对象来表示他们。
初始化Instance
我们继续看Instance的初始化。
| 1 | // 初始化底层Instance | 
初始化Instance需要一下几个元素:
- InstanceCallback类型的回调,用于底层执行结束后往上层回调。 - 1 
 2
 3
 4
 5
 6- struct InstanceCallback { 
 virtual ~InstanceCallback() {}
 virtual void onBatchComplete() {}
 virtual void incrementPendingJSCalls() {}
 virtual void decrementPendingJSCalls() {}
 };
- JSExecutorFactory类型的对象,用于生成JSExecutor用于真正执行JS。生产返回使用的是JSCExecutorFactory,返回JSIExecutor用于执行JS,调试使用的是RCTObjcExecutorFactory,返回RCTObjcExecutor通过websocket链接chrome执行JS。 
| 1 | class JSExecutorFactory { | 
- MessageQueueThread类型对象用于提供队列执行。这里是由RCTMessageThread来实现,内部用的是CFRunLoop来实现。
| 1 | //MessageQueueThread.h | 
- ModuleRegistry,这个包含native module信息的对象,它的来源就是我们上面看到的RCTModuleData。可以看到最终透传参数生成了RCTNativeModule
| 1 | //RCTCxxBridge.mm | 
有必要提一下,这上面的moduleData.instance,其实就是生成这个模块对应实例1
2
3
4
5
6
7- (instancetype)initWithModuleClass:(Class)moduleClass
                             bridge:(RCTBridge *)bridge
{
  return [self initWithModuleClass:moduleClass
                    moduleProvider:^id<RCTBridgeModule>{ return [moduleClass new]; }
                            bridge:bridge];
}
同时也会准备好它所对应的bridge和method queue1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 //RCTModuleData.mm
 - (void)setBridgeForInstance
{
  if ([_instance respondsToSelector:@selector(bridge)] && _instance.bridge != _bridge) {
    @try {
      [(id)_instance setValue:_bridge forKey:@"bridge"];
    }
    @catch (NSException *exception) {
      //...
    }
  }
}
- (void)setUpMethodQueue
{
  if (_instance && !_methodQueue && _bridge.valid) {
    BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
    if (implementsMethodQueue && _bridge.valid) {
      _methodQueue = _instance.methodQueue;
    }
    if (!_methodQueue && _bridge.valid) {
      _queueName = [NSString stringWithFormat:@"com.facebook.react.%@Queue", self.name];
      _methodQueue = dispatch_queue_create(_queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
      // assign it to the module
      if (implementsMethodQueue) {
        @try {
          [(id)_instance setValue:_methodQueue forKey:@"methodQueue"];
        }
        @catch (NSException *exception) {
          //...
        }
      }
    }
    RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  }
}
同时我们看到Instance::initializeBridge中生成了NativeToJsBridge,到这里Instance的初始化就结束了,下面进入NativeToJsBridge。
NativeToJsBridge
NativeToJsBridge作用主要是桥接Native和JS,它包含几个关键属性
- <JsToNativeBridge> m_delegate 
 JsToNativeBridge类型的引用,主要用于JS call Native
- <JSExecutor> m_executor 
 JSExecutor类型引用,主要用于执行Native call JS,这里实际使用是的是JSIExecutor(生产)/RCTObjcExecutor(调试)- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- std::shared_ptr<JSExecutorFactory> executorFactory; 
 if (!self.executorClass) {
 if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
 id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>) self.delegate;
 executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
 }
 if (!executorFactory) {
 //生产使用JSCExecutorFactory会生成JSIExecuror
 executorFactory = std::make_shared<JSCExecutorFactory>();
 }
 } else {
 //调试用RCTObjcExecutorFactory生成RCTObjcExecutor
 id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
 executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
 if (error) {
 [weakSelf handleError:error];
 }
 }));
 }
- <MessageQueueThread> m_executorMessageQueueThread 
 MessageQueueThread类型引用,由上层传递,用于队列管理
JSIExecutor
JSIExecutor主要用来Native call JS,包含几个主要属性:
- <jsi::Runtime> runtime_ 
 Runtime类型指针,代表JS的运行时。这是一个抽象类,其实际上是由JSCRuntime来实现的,JSCRuntime中的功能其实就是通过JavaScriptCode来完成(使用的C函数接口)。JSCRuntime上线了<jsi::Runtime>接口,提供了创建JS上下文的功能,同时可以执行JS。
| 1 | void JSCRuntime::evaluateJavaScript( | 
- <ExecutorDelegate> delegate_ 
 ExecutorDelegate类型的指针,这里的ExecutorDelegate是抽象类,实际是由JsToNative来实现的。也即JSIExecutor引用了JsToNative。
- <JSINativeModules> nativeModules_ 
 JSINativeModules由上层传入的ModuleRegistry构造而成,同时会将ModuleRegistry中包含的本地模块配置信息通过”__fbGenNativeModule”保存到JS端。
| 1 | //JSINativeModules.cpp | 
genModule会根据ModuleRegistry生成的module和method信息生成JS端的方法,结构类似:1
2
3
4
5
6{
  name: moduleName,
  module: {
    methodName: func
  }
}
JSIExecutor执行js方法的实现值得说下。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void JSIExecutor::callFunction(
    const std::string& moduleId,
    const std::string& methodId,
    const folly::dynamic& arguments) {
  if (!callFunctionReturnFlushedQueue_) {
    bindBridge();
  }
  auto errorProducer = [=] {
    std::stringstream ss;
    ss << "moduleID: " << moduleId << " methodID: " << methodId
       << " arguments: " << folly::toJson(arguments);
    return ss.str();
  };
  Value ret = Value::undefined();
  try {
    scopedTimeoutInvoker_(
        [&] {
          ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        },
        std::move(errorProducer));
  } catch (...) {
    std::throw_with_nested(
        std::runtime_error("Error calling " + moduleId + "." + methodId));
  }
  callNativeModules(ret, true);
}
其中callFunctionReturnFlushedQueue_来自和JS端的属性绑定1
2
3
4
5
6
7
8
9
10
11
12
13//JSIExecutor.cpp
Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "callFunctionReturnFlushedQueue");
//MessageQueue.js
callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) {
    this.__guard(() => {
      this.__callFunction(module, method, args);
    });
    return this.flushedQueue();
  }
可以看到callFunctionReturnFlushedQueue_会调用JS端的callFunctionReturnFlushedQueue方法,最终调用在JS端注册好的JS模块和方法。
而getPropertyAsFunction则是通过runtime来实现。(runtime是JS和Native沟通的桥梁)
JsToNativeBridge
JsToNativeBridge的实现就简单很多,直接通过ModuleRegistry注册好的native信息,调用对应模块的对应方法。
| 1 | void callNativeModules( | 
其中m_registry就是上层传入的ModuleRegistry对象
那JS Call Native的整套流程是怎样的呢?
 - JS调用MessageQueue.enqueueNativeCall1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22enqueueNativeCall(
    moduleID: number,
    methodID: number,
    params: any[],
    onFail: ?Function,
    onSucc: ?Function,
  ) {
    this._queue[MODULE_IDS].push(moduleID);
    this._queue[METHOD_IDS].push(methodID);
    this._queue[PARAMS].push(params);
    const now = Date.now();
    //MIN_TIME_BETWEEN_FLUSHES_MS = 5
    if (
      global.nativeFlushQueueImmediate &&
      now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS
    ) {
      const queue = this._queue;
      this._queue = [[], [], [], this._callID];
      this._lastFlush = now;
      global.nativeFlushQueueImmediate(queue);
    }
  }
可以看到5ms刷新一次
- nativeFlushQueueImmediate对应本地的方法1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20//JSIExecutor.cpp 
 runtime_->global().setProperty(
 *runtime_,
 "nativeFlushQueueImmediate",
 Function::createFromHostFunction(
 *runtime_,
 PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
 1,
 [this](
 jsi::Runtime&,
 const jsi::Value&,
 const jsi::Value* args,
 size_t count) {
 if (count != 1) {
 throw std::invalid_argument(
 "nativeFlushQueueImmediate arg count must be 1");
 }
 callNativeModules(args[0], false);
 return Value::undefined();
 }));
可以看到runtime_获取到全局上下文(即JS端的global),将nativeFlushQueueImmediate属性关联到本地callNativeModules方法。
这里值得一提的是,runtime_->global().setProperty用在很多将JS属性和native方法对象等绑定。
- callNativeModules1 
 2
 3
 4
 5//JSIExecutor.cpp 
 void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {
 delegate_->callNativeModules(
 *this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
 }
delegate_就是我们上面提到的<ExecutorDelegate>类型指针,实际就是JsToNativeBridge,最终也就走到了我们上面说的callNativeModules。
到这里,我们已经具备了native call js和js call native的能力。
总结
