一、为什么要新架构
因为现有架构有性能问题,具体如下:
- JS 层与 Native 的通讯依赖 Bridge转发
- Bridge转发通过线程切换传递JSON数据,并在单个工作线程内接收
- 滑动、动画等场景下高度计算、更新等高频UI变动使Bridge拥塞
- 界面出现白屏或卡顿
二、怎么做的
构建新的通信层,具体分为JSI、TurboModule、Fabric
- JSI
-
标准通信设施层,对旧架构Bridge的替代
- JS直连C++ Native层
- 旧架构有nativeLog这类函数也是直连,但是仅是部分函数实现,且依赖JavaScriptCore,不是整个通信设施层
- JSRuntime标准化,可选替代V8、Hermes
- JS直连C++ Native层
-
API 调用流程(以Android平台JS向JAVA的单向通信为例)
- 旧架构:JS->Bridge->JAVA
- 新架构:JS->JSI->C++->JNI->JAVA
- TurboModule
- 原生模块通信层,对旧架构NativeModule(官方文档上Native Modules这一节)的替代
- 通过JSI设施,完成原生模块定义和管理,让JS可调用原生模块,包括内置的、第三方原生库和应用侧自定义原生模块
- Fabric
- 原生UI通信层,对旧架构ViewManager(官方文档上Native Components这一节)的替代
- 通过JSI设施,完成原生UI的定义和管理,所定义出的UI模块,可在JSX使用
三、JSI示例
通过上面的简述,可以看到JSI是基础,已在0.60支持,旧架构下的三方库都可以通过它改造,完成直连。
- 原生能力的实现:在base64.h中定义,base64.cpp中实现,如base64_encode函数
// base64.h,函数声明
std::string base64_encode (std::string const& s, bool url = false);
- 原生能力安装入口:react-native-quick-base64.cpp
// cpp/react-native-quick-base64.cpp
// 安装函数,会在收集模块时被调用,完成原生能力的定义
void installBase64(jsi::Runtime& jsiRuntime) {
std::cout << "Initializing react-native-quick-base64" << "\n";
// 通过JSI提供的jsi::Function::createFromHostFunction创建可注入对象
auto base64FromArrayBuffer = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forAscii(jsiRuntime, "base64FromArrayBuffer"),
1, // string
[](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
std::string str;
if(!valueToString(runtime, arguments[0], &str)) {
return jsi::Value(-1);
}
// 实际的能力调用
std::string strBase64 = base64_encode(str);
// 前后做一些参数转换
return jsi::Value(jsi::String::createFromUtf8(runtime, strBase64));
}
);
// 将base64FromArrayBuffer注入到JSRuntime
// 此后在JS环境下,有一个叫base64FromArrayBuffer能在JS下使用
jsiRuntime.global().setProperty(jsiRuntime, "base64FromArrayBuffer", std::move(base64FromArrayBuffer));
...//后面还导出了一个base64ToArrayBuffer
- 初始化的时候安装:通过JNI提供到JAVA层,由JAVA层完成安装动作
// android/cpp-adapter.cpp JNI层提供QuickBase64Module_initialize到JAVA
extern "C"
JNIEXPORT void JNICALL
Java_com_reactnativequickbase64_QuickBase64Module_initialize(JNIEnv* env, jclass clazz, jlong jsiPtr) {
installBase64(*reinterpret_cast<facebook::jsi::Runtime*>(jsiPtr));
}
// android/src/main/java/com/reactnativequickbase64/QuickBase64Module.java
private static native void initialize(long jsiPtr, String docDir);
@Override
public void initialize() {
super.initialize();
// 模块初始化的时候完成初始化安装
QuickBase64Module.initialize(
this.getReactApplicationContext().getJavaScriptContextHolder().get(),
this.getReactApplicationContext().getFilesDir().getAbsolutePath());
}
- JS上的使用
// src/index.tsx
// 作为JS层函数fromByteArray导出给应用侧使用
export function fromByteArray(uint8: Uint8Array): string {
// 通过jsiRuntime.global().setProperty注入的base64FromArrayBuffer已在global可用
return base64FromArrayBuffer(uint8.buffer);
}
// 作为JS层函数btoa导出给应用侧使用
export function btoa(data: string): string {
return base64FromArrayBuffer(stringToArrayBuffer(data));
}
四、对应用侧的影响
目前看来,基本无影响,只会带来性能上的提升。
当然,三方库、自定义模块倒是需要更新才能享受新架构提升。
0.65代码库里已有JSI、TurboModule、Fabric相关代码,JSI、TurboModule已可用。
内部完全是用兼容的方式在开发,设了很多开关,可以看ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java文件,复杂性都留在了内部。
新架构从提出来到现在,已经三年多了,目前还没正式release,在线等。