本文由 简悦SimpRead 转码,原文地址 blog.nparashuram.com
由于 React Native 团队正在开发新的架构,有一些术语被用来描述......。
由于 React Native 团队正在开发新架构,有一些术语被用来描述各个部分。这篇文章旨在澄清其中的一些术语,并指出资源库中包含相关代码的地方。
桥接
在当前的 React Native 架构中,JavaScript 和 Java/ObjC 之间的通信是通过 "桥 "进行的。
- 这座桥是一个队列,用于在 JavaScript 和 Java/ObjC 之间发送编码为 JSON 字符串的消息。在每个 tick 期间,我们都会从队列的前端释放消息并对其进行处理。这种消息传递方式从根本上说是异步的。
- 桥接器还为 Java/ObjC 提供了一个接口,用于调度 JavaScript 的执行,通常用于本地模块的回调。
- 桥接器还与 React Native 的生命周期息息相关。启动或停止 React Native 通常意味着桥接器的初始化或拆卸。
为了更具体地了解桥接器,我们可以安装一个 MessageSpy 来查看来回发送的确切字节。还要注意的是,虽然网桥是异步的,但我们可以使用 @ReactMethod(isBlockingSynchronousMethod = true)进行一次性 synchronous method 调用。
虽然这种异步通信在大多数情况下都很不错,但在某些用例中,我们更希望 JavaScript 能同步在屏幕上绘制视图;而这正是新架构要解决的问题。
RPC
) Java/ObjC 方法。
打个比方,我们可以在浏览器中通过 JavaScript 调用 DOM 方法。例如,在语句 var el = document.createElement('div'); 中,变量 el 所引用的不是 JavaScript 对象,而是一个可能在 C++ 中实例化的对象。当 JavaScript 调用 el.setAttribute('width', 100) 时,我们最终会同步调用 C++ 中的 setWidth 方法,从而改变该元素的实际宽度。
在 React Native 中,我们同样可以使用 JavaScript 接口来调用用 Java/ObjC 实现的 UI 视图和本地模块上的方法。
下面的代码段展示了 JSI 的简单用法,以及我们如何将 Java/ObjC 对象暴露给 JavaScript。
// This sample is a Work in Progress for JSI , and specific functions may change.
#pragma once
#include <string>
#include <unordered_map>
#include <jsi/jsi.h>
// This SameplJSIObject needs to inheric from HostObject, and this is the object that will be exposed to JS.
// From JS, we can call various methods or access properties on this object.
// Look at https://github.com/facebook/react-native/blob/master/ReactCommon/jsi/jsi.h for more details
class JSI_EXPORT SampleJSIObject : public facebook::jsi::HostObject {
public:
// STEP 1: Expose "window.__SampleJSIObject" to JavaScript.
// This is a static function, typically called from Java or ObjC when the application is initialized.
// In java, jsi::Runtime &runtime is basically catalystInstance.getJavaScriptContextHolder()
static void SampleJSIObject::install(jsi::Runtime &runtime) {
runtime.global().setProperty(
runtime,
"__sampleJSIObject",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__SampleJSIObject"),
1,
[binding](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) {
// Whenever someone accesses __SampleJSIObject, this is what gets exposed.
return std::make_shared<SampleJSIObject>();
}));
}
// This is like the getter. Any property or method access from JS goes through this method.
// For example, when we call window.__sampleJSIObject.method1(), this method is called
jsi::Value TurboModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
std::string propNameUtf8 = propName.utf8(runtime);
// When window.__sampleJSIObject.method1() is called, propNameUtf8 == 'method1'
return jsi::Function::createFromHostFunction(
runtime,
propName, // Whats the name of the property that is called
argCount, // How many args does this method take
[](facebook::jsi::Runtime &rt, const facebook::jsi::Value &thisVal, const facebook::jsi::Value *args, size_t count) {
if (propNameUtf8 == 'method1') {
// Call a Java Method, that should be executed when window.__sampleJSIObject.method1() is called in JS.
// Note that we should convert jsi::Value *args to jni types
// The return type of Java Method should also be convereted back to jsi::Value
}
});
}
std::vector<PropNameID> getPropertyNames(Runtime& rt){
// Return a list of strings, that correspond to methods and properties
//
}
}
JSI 的大部分代码位于 React Native 中的 jsi 文件夹,使用 C++ 编写。
Fabric
Fabric 是已公布的重构架构的第一部分。虽然它只涉及新架构的用户界面,但有时会被错误地用来指代整个架构重构工作。
在当前架构中,所有用户界面操作(如创建原生视图、管理子模块等)都由一个名为 UIManagerModule 的原生模块处理。React Reconciller 通过桥接器发送用户界面命令,这些命令最终由该模块处理并委托给 UIImplementation。这反过来又会创建代表布局树的shadow nodes,并传递给 Yoga,以便根据从 JS 传递进来的 Flex 框样式确定相对坐标。
在新系统中,用户界面操作作为使用上述 JSI 接口的函数直接暴露给 JavaScript 。新的用户界面管理器可以为特定视图类型(如文本、视图或图像)创建组件描述符和阴影节点,然后与 Java/ObjC 通信,绘制平台特定的用户界面。
TurboModules
通过公开 JS 可以调用的函数,JSI 系统还可用于调用蓝牙或其他传感器等设备功能。这与浏览器公开 navigator.geolocation.getCurrentPosition 等函数的方式类似,这些函数在 JavaScript 中调用时,会触发浏览器中相应的 C++ 调用。
在当前系统中,创建了一个包含模块 名称和方法信息的表格。当 JS 调用特定本地模块时,模块和方法的索引会传递给 Java/ObjC,然后 Java/ObjC 调用特定方法。参数和返回值也会在 JavaScript 和 JNI/ObjC 对象之间进行转换。 在新系统中
- 我们公开一个JSI 对象顶级 "本地模块代理",名为
global.__turboModuleProxy - 要访问一个本地模块,比如 SampleTurboModule,应用程序代码将调用
require('NativeSampleTurboModule') - 在 NativeSampleTurboModule.js 中,我们调用 TurboModuleRegistry.getEnforcing()然后调用
global.__turboModuleProxy("SampleTurboModule") - 调用
global.__turboModuleProxy函数会触发我们在步骤 1 中暴露的 JSI 函数。平台分歧就发生在这里。 - 我们调用为 Java 和 ObjC 定义的 getModule 函数。该函数接收一个字符串,并返回特定 TurboModule 的 JSI 对象。
- 要获取 TurboModule JSI 对象,我们首先要获取 Java/ObjC 实现,然后从中 创建 JSI 对象。
现在我们有了 "SampleTurboModule "的 JSI 对象,可以从 JavaScript 中调用该 JSI 对象的方法。在调用过程中,我们还需要将 JSI 值转换为 JNI 参数,并在发送回结果时进行反向转换。 与当前架构一样,我们支持大多数类型,包括布尔、字符串、映射、数组、回调和承诺。
代码生成
在 TurboModule 和 Fabric 中,JavaScript 可以使用 Flow(或 TypeScript)定义可用接口。我们可以进一步利用这一接口定义来生成许多 C++ 类,以及 Java/ObjC 实现的接口/协议。例如,在 TurboModules 的情况下,可以生成封装 Java/ObjC 类并使用 JSI 对象公开方法的 C++ 类。
这将确保所有 JavaScript 调用在本地端都有可用的实现,并将通过代码推送等空中更新继续确保这一点。
结论
在向后兼容性方面,大多数 JavaScript 应用程序代码都不必因新架构而改变。为自定义视图管理器或本地模块编写的 Java/ObjC 代码必须更改,但其中许多代码都可以通过代码修改来使用新系统。还可以编写一个兼容层,让自定义视图管理器和本地模块在新系统中继续工作。
就时间安排而言,在撰写本篇文章时,大部分 JSI 代码已经进入了代码库。大量 Fabric 代码也已进入版本库,TurboModules 的更新也将继续推出。由于这主要是向后兼容,因此不一定要有一个单一的发布日期,更多的是逐步推出。您可以关注 React Native 代码库,以及有关 Fabric 和 TurboModules 的更新。