iOS中的RN的通信核心 NativeToJsBridge 对JS环境的初始化
1. NativeToJsBridge 的构造函数
在react-cxx中初始化_reactInstance时, 初始化了NativeToJsBridge:
// Instance 的 初始化 bridge 方法
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback, // std::make_unique<RCTInstanceCallback>(self),
std::shared_ptr<JSExecutorFactory> jsef, //executorFactory
std::shared_ptr<MessageQueueThread> jsQueue, //_jsMessageThread
std::shared_ptr<ModuleRegistry> moduleRegistry) {
...
jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
nativeToJsBridge_ = std::make_shared<NativeToJsBridge>(jsef.get(), moduleRegistry_, jsQueue, callback_);
nativeToJsBridge_->initializeRuntime();
/**
* After NativeToJsBridge is created, the jsi::Runtime should exist.
* Also, the JS message queue thread exists. So, it's safe to
* schedule all queued up js Calls.
*/
jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);
std::lock_guard<std::mutex> lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
}
// NativeToJsBridge 的构造方法
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory *jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false)),
m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
m_executorMessageQueueThread(std::move(jsQueue)),
m_inspectable(m_executor->isInspectable()) {}
NativeToJsBridge的构造方法必须在JSThread中执行!!! 以上代码中使用jsQueue->runOnQueueSync(std::function<> ...) 保证了!
在初始化完成以后, 主动调用NativeToJsBridge::initializeRuntime()方法主动初始化JS Context
先看一下NativeToJsBridge的几个成员变量:
class NativeToJsBridge {
...
std::shared_ptr<JsToNativeBridge> m_delegate;
std::unique_ptr<JSExecutor> m_executor;
std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;
}
在理解它之前, 需要先理解它的几个成员变量:
std::unique_ptr<JSExecutor> m_executor;: JS的执行器, 或者说JS Context 执行环境!!!std::shared_ptr<JsToNativeBridge> m_delegate;: This class manages calls from JS to native code.std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;: 前面提到过的JSThread的封装类, 为了模拟dispatch_queue, 保证JS的执行逻辑都在该线程执行.
2. JS执行环境JSExcutor的创建
我们看到成员变量JSExcutor在创建的时候, 大概会涉及以下几个方法和对象:
// 1. 在 RCTCxxBridge.start 方法中
std::shared_ptr<JSExecutorFactory> executorFactory = std::make_shared<JSCExecutorFactory>(installBindings);
// 2. 在 NativeToJsBridge 构造函数中
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)
// 3. JSCExecutorFactory 的实现方法
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> __unused jsQueue) {
return std::make_unique<JSIExecutor>(facebook::jsc::makeJSCRuntime(), delegate, JSIExecutor::defaultTimeoutInvoker, runtimeInstaller_);
}
// 4. JS 的 Runtime 实际是一个 JSCRuntime
std::unique_ptr<jsi::Runtime> makeJSCRuntime() {
return std::make_unique<JSCRuntime>();
}
// 5. JSCRuntime 是 JavaScriptCore API的包装实现!!!
class JSCRuntime : public jsi::Runtime {
public:
JSCRuntime();
JSCRuntime(JSGlobalContextRef ctx);
}
简单来说, JSExecutorFactory 会创建的JSExecutor, 底层是cxx类JSIExcutor的抽象, 具体落地实现的是JSCRuntime(iOS中包装 JavaScriptCore).
也就是说, 如果我们需要在native执行js语句, 原生方式使用JavaScriptCore API, RN完全封装到JSExecutor中了.
3. JSExcutor初始化js runtime, 注入关键函数!
前面提到, 在NativeToJsBridge在JSThread实例对象创建完成以后, 会初始化js runtime, 核心代码如下:
void NativeToJsBridge::initializeRuntime() {
runOnExecutorQueue([](JSExecutor *executor) mutable {
executor->initializeRuntime();
});
}
void NativeToJsBridge::runOnExecutorQueue(std::function<void(JSExecutor *)> task) {
if (*m_destroyed) {
return;
}
std::shared_ptr<bool> isDestroyed = m_destroyed;
m_executorMessageQueueThread->runOnQueue(
[this, isDestroyed, task = std::move(task)] {
if (*isDestroyed) {
return;
}
// 能到这里, 说明 this.m_excutor 还没有释放, 因此直接进行调用
// 另外再 task的执行过程中, m_excutor 一定会被保证不会被释放, 有以下几个原因
// 1. excutor 只会在 它被 unregistred 以后才会 destory
// 2. excutor unregistered 是在 JSThread 执行的!!!
// 3. 在下面 task 执行之前, 我们刚刚判断了 *isDestroyed !!! 表示一定没有 destory..
task(m_executor.get());
});
}
先看一下runOnExecutorQueue方法, 实现比较清晰! 但是这里有一点需要注意的地方:
- 使用局部变量
std::shared_ptr<bool> isDestroyed持有了m_destroyed. - 在异步将
std::function<void()> &&func加入到JSMessageQueue时, 构造的lambda会持有以下几个变量:this指针. 这里没有使用enable_from_this(), 是因为如果使用智能指针, 那么这个NativeToJsBridge不会主动释放. 这里这样实现的目标是, 如果NativeToJsBridge被释放了,JSMessageQueue才调度到这个task, 配合isDestory变量进行判断, 可以不用执行任务.isDestroyed单独用std::shared_ptr持有, 是为了防止 this 在task被调度时, 释放以后, 还能知道this是否有效.task = std::move(task), 是c++14的特性, 直接用移动构造给lambda形参.lambda任务是异步加入到JSMessageQueue中, 这个调度队列会在合适的时机调度.
接着executor->initializeRuntime(); 方法实际会调用JSIExecutor的具体方法:
// JSIExcutor 初始化 runtime 运行时...
void JSIExecutor::initializeRuntime() {
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));
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_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return nativeCallSyncHook(args, count); }));
if (runtimeInstaller_) {
runtimeInstaller_(*runtime_);
}
}
代码中主要做的是以下几个事情:
std::shared_ptr<jsi::Runtime> runtime_;引用的对象是JSCRuntime. 通俗点理解, 就是JS Context!!!runtime_->global()表示获取JS Context中的global对象.- 向
js global对象注入几个全局对象:global.nativeModuleProxy, 引用的是NativeModuleProxy, 它是NativeModule的代理, 通过它能调用到NativeModuleglobal.nativeFlushQueueImmediate, 它是提供给js的全局方法, 代码中的lambda是它的函数体!!! 函数体中的callNativeModules从字面量就知道, 是js调用native module的方法!!!gloabl.nativeCallSyncHook, 它是提供给js的全局方法, 代码中的lambda是它的函数体!!!
以上, JS contenxt 就搞定了!!!
注意, JS Context 创建, 以及 JS runtime 注入函数, 都是在 JSThread 中执行的!!!