2019-04-11
本文的原作者是 Kudo Chien,文章源链接为 How to write a React Native CxxModule 我将其翻译成中文。
CxxModule 是 React Native 中一个很少被提起的特性,你可以直接使用 C++ 编写 Native Module。
特别是对于 Android,给 C++ 写 JNI 封装供 Java 调用非常痛苦。
除此之外,CxxModule 的性能也更高,因为 Bridge 调用只在 JSVM 和 C++ 中发生,没有 JVM 的参与。
如果你想基于现有 C++ 代码开发 Native Module,你一定要试试 CxxModule。
在本文中,我会介绍 Android 的 CxxModule,由于 C++ 的跨平台特性,在 Obj-C 下也是一样的。
在 React Native 的源代码中包含一个 CxxModule 的范例。我(原作者)也写了一个 GitHub 示例。
通过重载 getMethods() 方法声明导出给 JavaScript 的方法:
auto HelloCxxModule::getMethods() -> std::vector<Method> { return { Method("foo", [](folly::dynamic args, Callback cb) { cb({"foo"}); }), }; }
其中:
通过重载 getConstants() 方法声明导出给 JavaScript 的常量:
auto HelloCxxModule::getConstants() -> std::map<std::string, folly::dynamic> { return { {"one", 1}, {"two", 2}, {"animal", "fox"}, }; }
从 C++ 发送 JS Event,通过 callJSFunction() 调用 RCTDeviceEventEmitter.emit():
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.h,比如如何导出一个返回 Promise 的方法。
现在已经知道了如何编写 CxxModule,但是还有一个重要部分在网络上没有人提到过——如何将 CxxModule 注册到 package 上面?
有一个 CxxModuleWrapper.java 类,它是注册用纯 C++ 编写的 Native Module 的最简单方法。
否则的话,你需要按照 hybrid 场景来,即代码将被 JS 和 Java 两者调用,请参考 Hybrid class。
让我们回到 CxxModuleWrapper.makeDso(),它接收两个参数:
代码如下(export.cpp):
extern "C" HelloCxxModule* createHelloCxxModule() { return new HelloCxxModule(); }
HelloCxxPackage.java:
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(); } }
现在你已经学会了 CxxModule 的基础用法,下面让我们进入最困难的部分。
我认为编译开源的 React Native 来接纳 CxxModule 这一部分是最难的。
这可能也是为什么没人讨论 CxxModule 的使用。
在上面的代码中,你会看到 CxxModule 严重依赖 Folly,这意味着必须使用 Folly 来构建出动态库(在 Android 上是 lib*.so)。
Folly 还依赖一些第三方库,例如 boost 和 glog。
尽管我们不需要重新构建这些库,但是我们仍然需要它们的头文件来构建我们的 CxxModule。
从根本上,这个过程非常类似于从代码构建 React Native 的过程。
我不会详细讲如何构建。请参见示例工程的 build.gradle 和 ndkbuild Android.mk。
为了将 CxxModule 链接到 React Native 中预先构建的库,我抽取了 react-native AAR 的 JNI。请参见 Gradle 的 prepareExtractedLibs。
总体上,你可以参见 HelloCxxModule 的提交记录。
实际上还有一些额外工作要做。
如果你的现有代码使用 C++ 编写的,为了复用这些代码,这些额外工作还是值得的。
在我们公司里,当我们做 Puffin Windows 版浏览器的时候,由于 React Native Windows 不支持 CxxModule,我们编写了大量封装代码。
例如,为了导出一个 native module 方法,我们需要:
如果你正在开发 Android APP,JNI 封装还是挺麻烦的,CxxModule 能省去你大量的封装样板代码。
另外,在 React Native 的新架构中,有一个新的 JSI 的设计,它貌似支持直接在 JS VM 中注册 JS 方法。我才到时候会更加容易。
最后,请让我宣传我们的产品——Puffin Browser on Windows。它非常快,打开大量标签也没有过多性能损耗。