本文由 简悦 SimpRead 转码, 原文地址 medium.com
CxxModule 是 React Native 中很少被提及的功能,你可以直接写 Native Module w......
CxxModule 是 React Native 中很少被提及的功能,你可以直接用 C++ 编写 Native Module。特别是在 Android 上,编写一个 JNI 封装器来完成 C++ 和 Java 之间的 marshaling 真的很麻烦。此外,由于桥接调用纯粹发生在 JSVM 和 C++ 之间,不涉及 JVM,因此性能应该会更好。如果你想编写一个本地模块,而你现有的代码又是 C++,那么你一定要试试 CxxModule。 我将以 Android 上的 CxxModule 为例进行介绍,因为对于 Obj-C 而言,C++ 的互操作曾经比较容易。
How to write a CxxModule
React Native 代码库 中有一个示例,可能已经有人知道了。我还在 GitHub 上写了一个简单的示例。
要为 JavaScript 导出方法,重载 getMethods():
auto HelloCxxModule::getMethods() -> std::vector<Method> {
return {
Method("foo", [](folly::dynamic args, Callback cb) { cb({"foo"}); }),
};
}
为 JavaScript 导出 foo() 方法,该方法将接受带有 "foo "的回调。
要为 JavaScript 导出常量,请覆盖 getConstants():
auto HelloCxxModule::getConstants() -> std::map<std::string, folly::dynamic> {
return {
{"one", 1}, {"two", 2}, {"animal", "fox"},
};
}
导出 JavaScript 常量
要从 C++ 输出 JS 事件,可以调用 RCTDeviceEventEmitter.emit() 的 JSFunction() 方法:
auto HelloCxxModule::getMethods() -> std::vector<Method> {
return {
Method("bar",
[this]() {
if (auto reactInstance = getInstance().lock()) {
reactInstance->callJSFunction(
"RCTDeviceEventEmitter", "emit",
folly::dynamic::array(
"appStateDidChange",
folly::dynamic::object("app_state", "active")));
}
}),
};
}
CxxModule 输出方法 bar(),该方法将发送 JS 事件以模拟 AppState 的变化。
更多详情,请查看 CxxModule.h,例如如何导出一个返回 Promise 的方法。
在了解了 CxxModule 的使用方法后,还有一个重要的部分在网上似乎无人提及--如何将 CxxModule 注册到软件包。
如果你的本地模块都是纯 C++ 的,有一个 CxxModuleWrapper.java,这是最简单的注册方法。否则,如果您需要混合情况,例如代码将同时从 JS 和 Java 调用,请参考 Hybrid class。 让我们回到 CxxModuleWrapper.makeDso(),它接受两个参数:一个是共享库文件名,另一个是导出的入口函数,用于创建 CxxModule 实例。
代码如下:
extern "C" HelloCxxModule* createHelloCxxModule() {
return new HelloCxxModule();
}
public final class HelloCxxPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
// I have librnpackage-hellocxx.so the exported createHelloCxxModule() above.
CxxModuleWrapper.makeDso("rnpackage-hellocxx", "createHelloCxxModule")
);
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
view raw
现在你可能知道 CxxModule 的一些基本形状了。让我们开始最困难的部分。
Build Process
我认为这部分是在开放源码软件 React Native 中采用 CxxModule 的最大难点。这可能是没有人谈论 CxxModule 使用的原因。
在上面的代码中,你可能会发现 CxxModule 严重依赖 Folly。这意味着你需要 Folly 将 CxxModule 构建为共享库(安卓上为 lib*.so)。Folly 还依赖于其他一些第三方库,如 boost 和 glog。尽管我们不需要重建这些库,但仍需要头文件来构建 CxxModule。 B̵a̵s̵i̵c̵a̵l̵l̵y̵,̵ ̵t̵h̵e̵ ̵p̵r̵o̵c̵e̵s̵s̵ ̵a̵r̵e̵ ̵p̵r̵e̵t̵t̵y̵ ̵m̵u̵c̵h̵ ̵l̵i̵k̵e̵ ̵b̵u̵i̵l̵d̵i̵n̵g̵ ̵R̵e̵a̵c̵t̵ ̵N̵a̵t̵i̵v̵e̵ ̵A̵n̵d̵r̵o̵i̵d̵ ̵f̵r̵o̵m̵ ̵s̵o̵u̵r̵c̵e̵ ̵c̵o̵d̵e̵. ̵ ̵̵I̵l̵l̵̵̵t̵̵g̵o̵̵t̵h̵r̵o̵̵̵g̵h̵̵̵b̵r̵i̵e̵̵̵l̵y̵̵̵h̵o̵̵̵̵t̵o̵̵̵b̵̵i̵l̵d̵.̵̵P̵l̵e̵a̵s̵e̵̵̵c̵h̵e̵̵c̵̵k̵ ̵b̵u̵i̵l̵d̵. ̵̵̵a̵d̵l̵e̵ ̵̵o̵m̵y̵̵̵s̵a̵m̵p̵l̵e̵̵̵̵̵j̵e̵̵̵t̵̵̵̵ ̵a̵n̵d̵ ̵̵t̵̵h̵e̵ ̵̵s̵a̵m̵p̵l̵e̵̵ ̵̵n̵d̵k̵̵b̵u̵i̵l̵d̵ ̵̵A̵n̵d̵̵o̵i̵d̵.̵m̵k̵̵
̵T̵o̵̵l̵i̵n̵k̵ ̵C̵x̵̵o̵M̵d̵u̵l̵e̵̵w̵i̵t̵h̵ ̵R̵e̵a̵c̵t̵̵̵N̵a̵t̵i̵v̵e̵ ̵p̵r̵e̵b̵u̵i̵l̵t̵ ̵l̵i̵b̵̵̵̵a̵̵i̵e̵s̵、 ̵ ̵I̵ ̵e̵x̵t̵r̵a̵c̵t̵̵h̵e̵̵ ̵J̵N̵I̵ ̵̵i̵l̵e̵̵s̵̵̵m00̵r̵e̵a̵c̵t̵-̵n̵a̵t̵i̵̵̵̵e̵ ̵̵A̵̵R̵. ̵ ̵S̵e̵̵r̵a̵d̵l̵e̵̵p̵r̵e̵p̵a̵r̵e̵E̵x̵t̵r̵a̵c̵t̵e̵d̵L̵b̵s̵ ̵t̵a̵s̵k̵ ̵00f̵o̵r̵ ̵d̵e̵t̵a̵i̵l̵.̵.
更新:我已经将示例更新为基于 React Native 0.60.4 的版本,为了更方便地修补 build.gradle,我转而从源代码构建 React Native Android AAR。这两个提交就是全部内容:
Summary
总的来说,您可以查看包含所有更改的提交,以添加 HelloCxxModule。
这确实需要一些额外的工作。但如果你的代码库大多是用 C++ 编写的,那么这些额外的工作还是值得做的。我所在的公司在 Windows 上开发 Puffin 浏览器时,由于 React Native Windows 不支持 CxxModule,我们不得不编写大量的封装代码。例如,要导出一个本地模块方法,我们应该做的是
- 将 C++ 代码封装为 C# 代码
- 在 C# 中封装方法,并在 DLL 中将 PInvoke 转换为 C 函数。
- 可选择将 JavaScript 中的方法封装到 NativeModules.foo 中。
如果你正在开发一个 Android 应用程序,那么 JNI 封装器就再简单不过了。 CxxModule 将为你省去大量的模板封装代码。
另一方面,下一个 React Native 架构 - Fabric,有一个新的 JSI 设计,似乎支持直接在 JS VM 中注册 JS 函数。我想,到时候生活会变得更轻松。
最后,请允许我推介一下我们的产品--Windows 上的 Puffin 浏览器。 它的速度非常快,如果你打开大量标签页,也不会有性能问题。