本文所有代码如果没有特别标注的话,默认用的都是 v0.76.0 的 RN 代码
JSI 是 React Native 新架构中的 JS ↔ Native 通信模型
JSI 解决了过去 Bridge 架构中 JSON 序列化开销、强制异步带来的一系列问题,为后续的 Turbo module、Fabric、RuntimeScheduler 系统的实现奠定了基础
本文将从其的设计理念,逐步深入到源码细节,跟读者一同探究 JSI 背后的原理
设计理念
JSI 全名 JavaScript Interface,直译为 JS 接口,它既不是系统,也不是通信协议,而是一个位于 C++ 层,面向 JS 的抽象接口
JSI 的设计理念用一句话总结的话就是:JSI 是一层 JS runtime 抽象,使 JS 与 C++ 可以直接共享对象与函数调用,从而实现无 Bridge 的高性能通信,并支持多 JS 引擎。
这句话引出了 JSI 的三个设计目标:
- 去 Bridge
- JS 与 C++ 能够直接互相操作
- JSI 与引擎无关,且可以替换引擎
JSI 是怎么实现这三个目标的呢?如果我们将 RN 中 JS 与 Native 的通信进行抽象,其中主要有 4 名角色,他们的关系如下:
Native <---通信---> C++ ---编写---> JS 引擎 ---创建---> JS runtime
其中 Native 与 C++ 的通信在本专栏的 RN 通信机制已经聊完了,这里就不赘述
我们重点看看 C++ -> JS 引擎 -> JS runtime 这条线:
- JS 引擎会提供 C++ embedding API,使我们能够在 C++ 中访问和操作 JS runtime 中的对象
- JS 引擎创建了 JS runtime,且对 JS 内部的变量、对象有完全的控制权
- 如果我们让 JS 引擎暴露 JS 对象的句柄,提供对象访问、函数调用、生命周期管理等接口,就可以实现 C++ 与 JS 在一个 Runtime 中共享对象的操作
了解了 JS 与 C++ 共享对象的原理后,我们知道如果要实现这一套通信机制,需要在 C++ 层面针对特定引擎提供的 embedding API 实现一个胶水层来管理,但我们也可以更进一步:提供一套接入标准(Interface),让不同的引擎来适配这套标准
如此一来,我们就可以达成第三个设计目标:引擎无关
这也是 JSI 名字的由来
JSI 的三个层级
接下来我们来聊聊 JSI 实现过程中的三个层级:
- 抽象层:JSI 中最接近引擎的层级,负责定义一套统一的接口让引擎接入,也可以当成 JSI 的 “能力清单”
- 服务层:负责在应用初始化期间协调需要 “安装” 在 runtime 中的能力
- 应用层:负责往 Runtime 中绑定具体的能力(bindings)
下面我们分别详细介绍三个层级具体做了什么
抽象层:JSI 的“能力清单”
如果把 JSI 比喻成一个工具的话,抽象层就是这个工具的 “使用说明书”
在抽象层中,最重要的入口文件就是 packages/react-native/ReactCommon/jsi/jsi/jsi.h
这个文件定义了两件事:
- JS 引擎需要实现的一组接口
- JS 值模型的 C++ 包装
文件所有类的分类如下:
// in jsi.h
// 1. 运行时与执行上下文 (Runtime & Context)
// 负责管理 JS 引擎实例及整体生命周期
class Runtime; // 核心引擎接口,所有 JS 操作都必须通过 Runtime 实例执行
class PreparedJavaScript; // 已编译/预处理的 JS 代码块,用于提高重复执行效率
// 2. JS 基础类型包装 (Base Value Types)
// JS 数据在 C++ 层的通用表示
class Value; // 顶层包装类,可表示 null, undefined, boolean, number, symbol, string, object
class Pointer; // 堆中 JS 对象的引用基类(所有受 GC 管理的对象基类)
// 3. 引用类型 (Reference Types)
// 继承自 Pointer,对应 JS 中的非原始类型
class PropNameID : public Pointer; // 属性名标识符,用于高效的属性访问
class Symbol : public Pointer; // JS Symbol 类型
class String : public Pointer; // JS String 类型
class BigInt : public Pointer; // JS BigInt 类型
class Object : public Pointer; // JS Object 类型
class Array : public Object; // JS Array 类型
class ArrayBuffer : public Object; // JS ArrayBuffer 类型
class Function : public Object; // JS Function 类型,支持从 C++ 调用 JS 函数
// 4. Buffer 类
// 用于处理原始字节序列
class Buffer; // 只读,用于传递脚本源码、静态资源
class StringBuffer : public Buffer; // 只读,通常用于将 C++ 字符串转换为 Buffer
class MutableBuffer; // 可写原始字节内存
// 5. 宿主扩展接口 (Host Interaction)
// 用于在 C++ 中实现 JS 可访问的对象或函数
class HostObject; // 接口类:继承此类可在 C++ 中自定义 JS 对象的属性拦截逻辑 (get/set)
class NativeState; // 挂在某个 JS Object 上的 Native 属性
HostFunctionType; // 类型别名:定义 C++ 函数如何被 JS 调用 (std::function 包装)
// 6. 异常与错误处理 (Error Handling)
// 处理 JS 与 C++ 边界处的异常
class JSIException; // JSI 异常的基类
class JSError : public JSIException; // 表示 JS 运行时的异常(包含堆栈信息,能被 JS 的 try...catch 捕获)
class JSINativeException : public JSIException; // JSI 宿主环境(C++ 侧)发生非 JS 逻辑导致的错误时抛出(不一定有 JS 堆栈)
// 7. 辅助与生命周期管理 (Utilities & RAII)
// 确保 C++ 与 JS 交互过程中的内存与逻辑安全
class Scope; // RAII 风格的作用域管理,批量释放局部引用
class WeakObject : public Pointer; // 对 JS 对象的弱引用,不阻止 GC 回收
class Instrumentation; // 提供运行时性能指标和内存使用情况的接口
如果一个 JS 引擎想要嵌入 RN ,它至少需要提供与这份 “能力清单” 兼容的 Runtime 实现,具体的引擎侧实现可以参考 hermes 代码库的 API/hermes/hermes.cpp 文件(引擎侧的内容超过了本专栏的范围,这里就不展开了)
服务层:JSI 的管家 ReactInstance
如果把 JSI 比喻成工具的话,服务层就是该工具的管理员
一旦引擎实现了所有 JSI 要求的能力并且成功在 RN 中创建了 Runtime 实例后,下一步就是根据这个 Runtime 搭建出可以由 RN 调度且可以安全调用的执行环境
负责搭建执行环境的类叫做 ReactInstance,它就像 RN 中负责协调 Runtime 的管家,它的主要方法有:
-
构造函数
ReactInstance::ReactInstance,负责创建 4 个关键角色:-
RuntimeExecutor:负责在 Runtime 中运行一些任务;确保所有 JS 调用都在 JS thread 中执行;负责把 runtime 接进异常处理
-
runtimeExecutorThatWaitsForInspectorSetup:负责把 runtime 接进 inspector
-
RuntimeScheduler:负责管理 Runtime 的事件循环,这个后面会有文章专门讲解
-
BufferedRuntimeExecutor:带有优先级调度的 RuntimeExecutor
-
-
ReactInstance::initializeRuntime:负责初始化 Runtime;调度 JSI 应用层 binding 所需的功能 -
ReactInstance::callFunctionOnModule:Native 侧调用 JS 方法的入口 -
ReactInstance::loadScript:加载 JS 代码,并执行积压的 JS 调用
下面是详细的代码解释:
ReactInstance::ReactInstance
// in packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp
ReactInstance::ReactInstance(
// 接收的参数,主要看这个 runtime 就好,runtime 是被创建好了之后传入的,ReactInstance 并不负责创建 runtime
std::unique_ptr<JSRuntime> runtime,
std::shared_ptr<MessageQueueThread> jsMessageQueueThread,
std::shared_ptr<TimerManager> timerManager,
JsErrorHandler::OnJsError onJsError,
jsinspector_modern::HostTarget* parentInspectorTarget)
: runtime_(std::move(runtime)),
jsMessageQueueThread_(jsMessageQueueThread),
timerManager_(std::move(timerManager)),
jsErrorHandler_(std::make_shared<JsErrorHandler>(std::move(onJsError))),
parentInspectorTarget_(parentInspectorTarget) {
// 这里的 runtimeExecutor 是一个 C++ 的 Lambda 表达式
// 它的主要目标有 2:
// 1. 确保交给他执行的 callback 都在 JS 线程中执行
// 2. 确保 JS 线程正常运行且能捕获运行期间发生的错误
RuntimeExecutor runtimeExecutor = [weakRuntime = std::weak_ptr(runtime_),
weakTimerManager =
std::weak_ptr(timerManager_),
weakJsThread =
std::weak_ptr(jsMessageQueueThread_),
jsErrorHandler =
jsErrorHandler_](auto callback) {
// 如果 Runtime 没了,直接返回
if (weakRuntime.expired()) {
return;
}
// 如果当前有一个致命的 JS error,禁止执行其他代码
if (!jsErrorHandler->isRuntimeReady() &&
jsErrorHandler->hasHandledFatalError()) {
LOG(INFO)
<< "RuntimeExecutor: Detected fatal error. Dropping work on non-js thread."
<< std::endl;
return;
}
// 确保 JS 线程还存在
if (auto jsThread = weakJsThread.lock()) {
// 将另一个 Lambda 投入到 JS 线程执行
jsThread->runOnQueue([jsErrorHandler,
weakRuntime,
weakTimerManager,
callback = std::move(callback)]() {
// 由于这个 Lambda 在进入线程被执行前需要在 queue 中等待
// 所以我们在执行之前需要再确认一下这个 Runtime 是否还在运行
auto runtime = weakRuntime.lock();
if (!runtime) {
return;
}
// 这里的 runtime 其实是 C++ 的一层 wrapper,实际上执行 js 代码的是 jsiRuntime
jsi::Runtime& jsiRuntime = runtime->getRuntime();
SystraceSection s("ReactInstance::_runtimeExecutor[Callback]");
try {
// 真正执行 callback 的代码
callback(jsiRuntime);
// 在默认的 0.76.0 + hermes 下,这个 flag 始终为 true,代表由引擎来处理微任务的调度
// 如果是从其他版本升级上来的话,需要用到这段代码,这本质就是把之前用 setImmediate 模拟微任务那一套包装成 timerManager,然后在这里清空一下模拟的 “微任务队列”
if (!ReactNativeFeatureFlags::enableMicrotasks()) {
if (auto timerManager = weakTimerManager.lock()) {
timerManager->callReactNativeMicrotasks(jsiRuntime);
}
}
} catch (jsi::JSError& originalError) {
// 如果执行过程中有错,这里需要兜住
jsErrorHandler->handleFatalError(jsiRuntime, originalError);
}
});
}
};
// 如果需要接入 inspector 才进入这段逻辑
if (parentInspectorTarget_) {
auto executor = parentInspectorTarget_->executorFromThis();
// 上面 runtimeExecutor 的装饰器,主要作用就是在下面 executor 方法执行完之前不要执行传进来的 callback
auto runtimeExecutorThatWaitsForInspectorSetup =
std::make_shared<BufferedRuntimeExecutor>(runtimeExecutor);
// 核心逻辑
executor([this, runtimeExecutor, runtimeExecutorThatWaitsForInspectorSetup](
jsinspector_modern::HostTarget& hostTarget) {
// 把当前的 ReactInstance 绑定到 hostTarget
// hostTarget 负责管理调试会话,把当前 ReactInstance 跟 Runtime 暴露给基于 CDP(Chrome DevTools Protocol)的调试工具
inspectorTarget_ = &hostTarget.registerInstance(*this);
// 绑定当前 Runtime
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(
runtime_->getRuntimeTargetDelegate(), runtimeExecutor);
// 把积压的 callback 一次 flush 了
runtimeExecutorThatWaitsForInspectorSetup->flush();
});
// 用当前的 runtimeExecutorThatWaitsForInspectorSetup 替代上面的 runtimeExecutor
// 主要目的就是为了等上面的 executor 执行完,一旦执行完,其他行为与之前的 runtimeExecutor 没有区别
runtimeExecutor =
[runtimeExecutorThatWaitsForInspectorSetup](
std::function<void(jsi::Runtime & runtime)>&& callback) {
runtimeExecutorThatWaitsForInspectorSetup->execute(
std::move(callback));
};
}
// 构造 RuntimeScheduler
runtimeScheduler_ = std::make_shared<RuntimeScheduler>(
runtimeExecutor,
RuntimeSchedulerClock::now,
[jsErrorHandler = jsErrorHandler_](
jsi::Runtime& runtime, jsi::JSError& error) {
jsErrorHandler->handleFatalError(runtime, error);
});
// 用来监控一些性能指标
runtimeScheduler_->setPerformanceEntryReporter(
PerformanceEntryReporter::getInstance().get());
// 构造 BufferedRuntimeExecutor
// 本质上也是 runtimeExecutor 的装饰器,区别在于它用上了 runtimeScheduler 提供的能力
// 所以他可以进行优先级调度
bufferedRuntimeExecutor_ = std::make_shared<BufferedRuntimeExecutor>(
[runtimeScheduler = runtimeScheduler_.get()](
std::function<void(jsi::Runtime & runtime)>&& callback) {
runtimeScheduler->scheduleWork(std::move(callback));
});
}
ReactInstance::initializeRuntime
// in packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp
void ReactInstance::initializeRuntime(
JSRuntimeFlags options,
BindingsInstallFunc bindingsInstallFunc) noexcept {
// 借助 runtimeScheduler_ 来执行这个 lambda
runtimeScheduler_->scheduleWork([this, options, bindingsInstallFunc](
jsi::Runtime& runtime) {
SystraceSection s("ReactInstance::initializeRuntime");
// 在 runtime 的 global 上绑定一个 native 的高精度时间能力
bindNativePerformanceNow(runtime);
// 在 runtime 的 global 上绑定 RuntimeScheduler 需要的能力
RuntimeSchedulerBinding::createAndInstallIfNeeded(
runtime, runtimeScheduler_);
// 给当前 runtime 注册 profiler 并绑定当前线程,用于性能分析
runtime_->unstable_initializeOnJsThread();
// 把一些 Native 的 flag 挂到 global 上
defineReactInstanceFlags(runtime, options);
// 把异常处理相关方法挂到 global
defineReadOnlyGlobal(
runtime,
"RN$handleException",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "handleException"),
2,
[jsErrorHandler = jsErrorHandler_](
jsi::Runtime& runtime,
const jsi::Value& /*unused*/,
const jsi::Value* args,
size_t count) {
// 省略部分代码
}));
// 用来让 JS 侧注册可以被 Native 调用的 module
// 这样 Native 就可以调用 JS 的 module 了
defineReadOnlyGlobal(
runtime,
"RN$registerCallableModule",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "registerCallableModule"),
2,
[this](
jsi::Runtime& runtime,
const jsi::Value& /*unused*/,
const jsi::Value* args,
size_t count) {
// 省略部分代码
// 这里有个细节,callableModules_ 中的 value(第二个参数)并不是模块方法本身
// 它代表的是一个 return 模块方法的方法
// 这个做法可以实现模块的懒加载
callableModules_.emplace(
std::move(name),
args[1].getObject(runtime).getFunction(runtime));
return jsi::Value::undefined();
}));
// 把 setTimeout、clearTimeout、setInterval、
// clearInterval、requestAnimationFrame、cancelAnimationFrame
// 这些方法挂到 global 上,让 JS 可以调用,这些方法实现都在
// packages/react-native/ReactCommon/react/runtime/TimerManager.cpp
timerManager_->attachGlobals(runtime);
// 最后绑定平台自己的 bindings
bindingsInstallFunc(runtime);
});
}
ReactInstance::callFunctionOnModule
// in packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp
void ReactInstance::callFunctionOnModule(
const std::string& moduleName,
const std::string& methodName,
folly::dynamic&& args) {
// 用 bufferedRuntimeExecutor_ 来调度
bufferedRuntimeExecutor_->execute([this,
moduleName = moduleName,
methodName = methodName,
args = std::move(args)](
jsi::Runtime& runtime) {
SystraceSection s(
"ReactInstance::callFunctionOnModule",
"moduleName",
moduleName,
"methodName",
methodName);
auto it = callableModules_.find(moduleName);
// 处理找不到 moduleName 的情况
if (it == callableModules_.end()) {
std::ostringstream knownModules;
int i = 0;
for (it = callableModules_.begin(); it != callableModules_.end();
it++, i++) {
const char* space = (i > 0 ? ", " : " ");
knownModules << space << it->first;
}
throw jsi::JSError(
runtime,
"Failed to call into JavaScript module method " + moduleName + "." +
methodName +
"(). Module has not been registered as callable. Registered callable JavaScript modules (n = " +
std::to_string(callableModules_.size()) +
"):" + knownModules.str() +
". Did you forget to call `registerCallableModule`?");
}
// 如果当前模块没有被初始化过(第一次调用),需要加载该模块
if (std::holds_alternative<jsi::Function>(it->second)) {
auto module =
std::get<jsi::Function>(it->second).call(runtime).asObject(runtime);
it->second = std::move(module);
}
// 取得调用模块名字
auto& module = std::get<jsi::Object>(it->second);
// 取得调用模块方法
auto method = module.getPropertyAsFunction(runtime, methodName.c_str());
// 构造参数
std::vector<jsi::Value> jsArgs;
for (auto& arg : args) {
jsArgs.push_back(jsi::valueFromDynamic(runtime, arg));
}
// 调用!
method.callWithThis(
runtime, module, (const jsi::Value*)jsArgs.data(), jsArgs.size());
});
}
ReactInstance::loadScript
// in packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp
void ReactInstance::loadScript(
std::unique_ptr<const JSBigString> script,
const std::string& sourceURL) {
auto buffer = std::make_shared<BigStringBuffer>(std::move(script));
std::string scriptName = simpleBasename(sourceURL);
// 用 runtimeScheduler_ 来调度
runtimeScheduler_->scheduleWork(
[this,
scriptName,
sourceURL,
buffer = std::move(buffer),
// 这里用 weak_ptr 是不希望这一个 lambda 延长 weakBufferedRuntimeExecuter 的生命周期
// 不然可能出现 runtime 没了,但是 Executer 还在的尴尬情况
weakBufferedRuntimeExecuter = std::weak_ptr<BufferedRuntimeExecutor>(
bufferedRuntimeExecutor_)](jsi::Runtime& runtime) {
SystraceSection s("ReactInstance::loadScript");
// 省略部分代码
// 核心代码!执行 JS bundle
runtime.evaluateJavaScript(buffer, sourceURL);
// 处理异常情况
if (!jsErrorHandler_->hasHandledFatalError()) {
jsErrorHandler_->setRuntimeReady();
}
// 省略部分代码
// 判断 runtime 是否还在
// 如果是则调度执行 JS bundle 期间积压的任务
if (auto strongBufferedRuntimeExecuter =
weakBufferedRuntimeExecuter.lock()) {
strongBufferedRuntimeExecuter->flush();
}
});
}
应用层:JSI 的消费者
如果把 JSI 比喻为工具的话,应用层就是该工具的使用者
JSI 的抽象层定义跨引擎统一的 JavaScript 运行时接口;服务层基于这些接口为 React Native 提供调度、模块和 UI 等运行时能力;应用层则通过 binding 建立 JS Runtime 与 Host(Native) Runtime 之间能力暴露机制,是的双方都可以互相调用彼此的能力
上面这句话可能有点抽象,我们用一个 binding 例子来说明一下
还记得我们上一小节讨论的 ReactInstance::initializeRuntime 的方法吗?在第 15 行,它调用了 RuntimeSchedulerBinding::createAndInstallIfNeeded 方法
这个方法就是一个 binding,让我们来看看它 bind 了什么:
// in packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeSchedulerBinding.cpp
std::shared_ptr<RuntimeSchedulerBinding>
RuntimeSchedulerBinding::createAndInstallIfNeeded(
jsi::Runtime& runtime,
const std::shared_ptr<RuntimeScheduler>& runtimeScheduler) {
auto runtimeSchedulerModuleName = "nativeRuntimeScheduler";
// 试图从 runtime 的 global 中拿到 nativeRuntimeScheduler 对象
auto runtimeSchedulerValue =
runtime.global().getProperty(runtime, runtimeSchedulerModuleName);
// 如果不成功,就自己创建一个
if (runtimeSchedulerValue.isUndefined()) {
// 创建了一个 RuntimeSchedulerBinding 类的实例
auto runtimeSchedulerBinding =
std::make_shared<RuntimeSchedulerBinding>(runtimeScheduler);
// 通过 JSI 的 createFromHostObject 方法把 runtimeSchedulerBinding 包装成一个 JS 可以调用的对象
auto object =
jsi::Object::createFromHostObject(runtime, runtimeSchedulerBinding);
// 把 JS 可以调用的对象挂到 global 的 nativeRuntimeScheduler 对象下
runtime.global().setProperty(
runtime, runtimeSchedulerModuleName, std::move(object));
return runtimeSchedulerBinding;
}
// 省略部分代码
}
可以看到,这个 RuntimeSchedulerBinding 本质上就是把与自己同名的类实例化后 “绑定” 到 JS Runtime 的 global 对象上
正常来说 C++ 的对象并不能直接挂到 JS 的对象上,但是通过 jsi::Object::createFromHostObject 方法的封装,object 对象得以顺利把自己 “绑定” 到 JS Runtime 的 global 对象上
jsi::Object::createFromHostObject 就是 JSI 定义的其中一个能力,它由需要接入的引擎自己实现,而 binding 是基于这一底层能力,做了上层的具体能力封装(在 RuntimeSchedulerBinding 例子中是任务调度能力的封装)
在 RuntimeSchedulerBinding 这个例子里,它对 JS 暴露了自己类里的方法;与之相对的,JS 也可以通过类似方法向 Native 暴露自己的能力,所以我才说 binding 是 JS Runtime 与 Host(Native) Runtime 之间能力暴露机制
至于 RuntimeSchedulerBinding 具体暴露了哪些能力,本专栏的下一篇关于 RuntimeScheduler 的文章会有详细说明,本文就不展开了
总结
JSI 通过分层的方式构建了 React Native 新架构中的 JavaScript 运行时体系:抽象层定义了跨引擎统一的运行时能力接口,服务层在这些能力之上构建并管理 React Native 的调度、模块与渲染等核心运行机制,而应用层则通过各类 bindings 使用这些能力来实现具体的 UI 和业务逻辑
三者共同构成了一条从底层运行时能力到上层应用功能的完整链路,使 JS 与 Native 能够在同一运行时环境中高效协作