legacy TuboCxxModule是CxxModule适配Turbo架构的旧模式,本篇基于[ReactNative笔记]my-mod适配CxxModule的结果适配
参考SampleTurboCxxModuleLegacyImpl.h和SampleTurboCxxModuleLegacyImpl.cpp
准备环境
参考[ReactNative笔记]my-mod适配CxxModule
创建my-mod
参考[ReactNative笔记]my-mod适配CxxModule
基于CxxModule适配legacy TurboCxxModule
适配JS/TS
- 新增NativeMyMod.ts到
src文件夹
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
multiply(a: number, b: number, callback: (result: number) => void): void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MyMod');
- 修改index.tsx
const MyMod = require('./NativeMyMod').default;
export function multiply(a: number, b: number): Promise<number> {
return new Promise(resolve => MyMod.multiply(a, b, resolve));
}
- 添加codegenConfig到
package.json
+ },
+ "codegenConfig": {
+ "name": "RNMyModSpec",
+ "type": "modules",
+ "jsSrcsDir": "src"
}
}
适配Android
- 修改MyModPackage.java
package com.mymod;
import androidx.annotation.Nullable;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.CxxModuleWrapper;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import java.util.HashMap;
import java.util.Map;
public class MyModPackage extends TurboReactPackage {
@Nullable
@Override
public NativeModule getModule(String name, ReactApplicationContext reactApplicationContext) {
if (name.equals(MyModModule.NAME)) {
return MyModModule.makeDso("cpp", "createMyModCxxModule");
} else {
return null;
}
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return () -> {
final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
moduleInfos.put(
MyModModule.NAME,
new ReactModuleInfo(
MyModModule.NAME,
"MyModModule",
false,
false,
false,
CxxModuleWrapper.class.isAssignableFrom(MyModModule.class),
TurboModule.class.isAssignableFrom(MyModModule.class))
);
return moduleInfos;
};
}
}
- 重写MyModModule.java
package com.mymod;
import com.facebook.jni.HybridData;
import com.facebook.react.bridge.CxxModuleWrapper;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import com.facebook.soloader.SoLoader;
public class MyModModule extends CxxModuleWrapper implements TurboModule {
public static final String NAME = "MyMod";
protected MyModModule(HybridData hd) {
super(hd);
}
private static native MyModModule makeDsoNative(String var0, String var1);
public static MyModModule makeDso(String library, String factory) {
SoLoader.loadLibrary(library);
String soPath = SoLoader.unpackLibraryAndDependencies(library).getAbsolutePath();
return makeDsoNative(soPath, factory);
}
}
- 修改CMakeLists.txt
add_library(${CMAKE_PROJECT_NAME}
SHARED
../cpp/react-native-my-mod.cpp
+ cpp-adapter.cpp
)
# Specifies a path to native header files.
@@ -20,9 +21,11 @@ target_include_directories(${CMAKE_PROJECT_NAME}
)
target_link_libraries(${CMAKE_PROJECT_NAME}
+ fbjni
folly_runtime
- common_flags
+ glog
react_nativemodule_core
+ common_flags
)
- 重写cpp-adapter.cpp
#include <react/jni/CxxModuleWrapper.h>
#include <glog/logging.h>
#include <dlfcn.h>
using namespace facebook::jni;
using namespace facebook::xplat::module;
using namespace facebook::react;
namespace mymodule {
class MyModModule
: public HybridClass<MyModModule, CxxModuleWrapper> {
public:
constexpr static const char *const kJavaDescriptor =
"Lcom/mymod/MyModModule;";
static void registerNatives() {
registerHybrid(
{makeNativeMethod("makeDsoNative", MyModModule::makeDsoNative)});
}
static local_ref<MyModModule::javaobject> makeDsoNative(
alias_ref<jclass>,
const std::string& soPath,
const std::string& fname) {
// soPath is the path of a library which has already been loaded by
// java SoLoader.loadLibrary(). So this returns the same handle,
// and increments the reference counter. We can't just use
// dlsym(RTLD_DEFAULT, ...), because that crashes on 4.4.2 and
// earlier: https://code.google.com/p/android/issues/detail?id=61799
void* handle = dlopen(soPath.c_str(), RTLD_NOW);
if (!handle) {
throwNewJavaException(
gJavaLangIllegalArgumentException,
"module shared library %s is not found",
soPath.c_str());
}
// Now, arrange to close the handle so the counter is decremented.
// The handle will remain valid until java closes it. There's no
// way to do this on Android, but that's no reason to be sloppy
// here.
auto guard = folly::makeGuard([&] { CHECK(dlclose(handle) == 0); });
void* sym = dlsym(handle, fname.c_str());
if (!sym) {
throwNewJavaException(
gJavaLangIllegalArgumentException,
"module function %s in shared library %s is not found",
fname.c_str(),
soPath.c_str());
}
auto factory = reinterpret_cast<CxxModule* (*)()>(sym);
return MyModModule::newObjectCxxArgs(
std::unique_ptr<CxxModule>((*factory)()));
}
protected:
friend HybridBase;
using HybridBase::HybridBase;
};
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
return initialize(
vm, [] { mymodule::MyModModule::registerNatives(); });
}
- 配置example/android/gradle.properties
-newArchEnabled=false
+newArchEnabled=true
适配iOS
- 修改MyMod.h
#import <React/RCTCxxModule.h>
+#import <ReactCommon/RCTTurboModule.h>
-@interface MyMod : RCTCxxModule
+@interface MyMod : RCTCxxModule <RCTTurboModule>
- (std::unique_ptr<facebook::xplat::module::CxxModule>)createModule;
- 修改MyMod.mm
return std::make_unique<mymod::MyModCxxModule>();
}
+- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
+ return nullptr;
+}
+
@end
- 配置example/ios/Podfile
+ENV['RCT_NEW_ARCH_ENABLED'] = '1'
+
# Resolve react_native_pods.rb with node to allow for hoisting
测试适配结果
- Android运行my-mod
$ yarn example start &
$ yarn example android
- iOS运行my-mod
$ pushd example/ios && pod install && popd
$ yarn example start &
$ yarn example ios
JS/C++的类型/方法转换
std::vector<CxxModule::Method> SampleTurboCxxModuleLegacyImpl::getMethods() {
return {
CxxModule::Method(
"voidFunc", [this](folly::dynamic args) { voidFunc(); }),
CxxModule::Method(
"getBool",
[this](folly::dynamic args) {
return getBool(xplat::jsArgAsBool(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getEnum",
[this](folly::dynamic args) {
return getEnum(xplat::jsArgAsDouble(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getNumber",
[this](folly::dynamic args) {
return getNumber(xplat::jsArgAsDouble(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getString",
[this](folly::dynamic args) {
return getString(xplat::jsArgAsString(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getString",
[this](folly::dynamic args) {
return getString(xplat::jsArgAsString(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getArray",
[this](folly::dynamic args) {
return getArray(xplat::jsArgAsArray(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getObject",
[this](folly::dynamic args) {
return getObject(xplat::jsArgAsObject(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getUnsafeObject",
[this](folly::dynamic args) {
return getUnsafeObject(xplat::jsArgAsObject(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getRootTag",
[this](folly::dynamic args) {
return getNumber(xplat::jsArgAsDouble(args, 0));
},
CxxModule::SyncTag),
CxxModule::Method(
"getValue",
[this](folly::dynamic args) {
return getValue(
xplat::jsArgAsDouble(args, 0),
xplat::jsArgAsString(args, 1),
xplat::jsArgAsObject(args, 2));
},
CxxModule::SyncTag),
CxxModule::Method(
"getValueWithCallback",
[this](folly::dynamic args, CxxModule::Callback callback) {
getValueWithCallback(callback);
}),
CxxModule::Method(
"getValueWithPromise",
[this](
folly::dynamic args,
CxxModule::Callback resolve,
CxxModule::Callback reject) {
getValueWithPromise(xplat::jsArgAsBool(args, 0), resolve, reject);
}),
};
};
folly::dynamic和jsi::Value类型转换
jsi::Value TurboCxxModule::invokeMethod(
jsi::Runtime &runtime,
const std::string &methodName,
const jsi::Value *args,
size_t count) {
auto it = cxxMethods_.begin();
for (; it != cxxMethods_.end(); it++) {
auto method = *it;
if (method.name == methodName) {
break;
}
}
if (it == cxxMethods_.end()) {
throw std::runtime_error(
"Function '" + methodName + "' cannot be found on cxxmodule: " + name_);
}
auto method = *it;
if (method.syncFunc) {
auto innerArgs = folly::dynamic::array();
for (size_t i = 0; i < count; i++) {
innerArgs.push_back(jsi::dynamicFromValue(runtime, args[i]));
}
return jsi::valueFromDynamic(
runtime, method.syncFunc(std::move(innerArgs)));
} else if (method.func && !method.isPromise) {
// Async method.
CxxModule::Callback first;
CxxModule::Callback second;
if (count < method.callbacks) {
throw std::invalid_argument(folly::to<std::string>(
"Expected ",
method.callbacks,
" callbacks, but only ",
count,
" parameters provided"));
}
if (method.callbacks == 1) {
auto wrapper = CallbackWrapper::createWeak(
args[count - 1].getObject(runtime).getFunction(runtime),
runtime,
jsInvoker_);
first = makeTurboCxxModuleCallback(runtime, wrapper);
} else if (method.callbacks == 2) {
auto wrapper1 = CallbackWrapper::createWeak(
args[count - 2].getObject(runtime).getFunction(runtime),
runtime,
jsInvoker_);
auto wrapper2 = CallbackWrapper::createWeak(
args[count - 1].getObject(runtime).getFunction(runtime),
runtime,
jsInvoker_);
first = makeTurboCxxModuleCallback(runtime, wrapper1);
second = makeTurboCxxModuleCallback(runtime, wrapper2);
}
auto innerArgs = folly::dynamic::array();
for (size_t i = 0; i < count - method.callbacks; i++) {
innerArgs.push_back(jsi::dynamicFromValue(runtime, args[i]));
}
method.func(std::move(innerArgs), first, second);
} else if (method.isPromise) {
return createPromiseAsJSIValue(
runtime,
[method, args, count, this](
jsi::Runtime &rt, std::shared_ptr<Promise> promise) {
auto resolveWrapper = CallbackWrapper::createWeak(
promise->resolve_.getFunction(rt), rt, jsInvoker_);
auto rejectWrapper = CallbackWrapper::createWeak(
promise->reject_.getFunction(rt), rt, jsInvoker_);
CxxModule::Callback resolve =
makeTurboCxxModuleCallback(rt, resolveWrapper);
CxxModule::Callback reject =
makeTurboCxxModuleCallback(rt, rejectWrapper);
auto innerArgs = folly::dynamic::array();
for (size_t i = 0; i < count; i++) {
innerArgs.push_back(jsi::dynamicFromValue(rt, args[i]));
}
method.func(std::move(innerArgs), resolve, reject);
});
}
return jsi::Value::undefined();
}