pure TuboCxxModule是CxxModule适配Turbo架构的新模式,本篇基于[ReactNative笔记]my-mod适配legacy CxxModule的结果适配
准备环境
参考[ReactNative笔记]my-mod适配legecy TurboCxxModule
创建my-mod
参考[ReactNative笔记]my-mod适配legecy TurboCxxModule
基于legacy TurboCxxModule适配pure TurboCxxModule
适配C++
- 修改react-native-my-mod.h
-#include <cxxreact/CxxModule.h>
+#include "RNMyModSpecJSI.h"
-using namespace facebook::xplat::module;
+using namespace facebook::react;
+using namespace facebook::jsi;
namespace mymod {
-class MyModCxxModule : public CxxModule {
+class MyModCxxModule : public NativeMyModCxxSpecJSI {
public:
- MyModCxxModule() = default;
+ MyModCxxModule(std::shared_ptr<CallInvoker> jsInvoker);
- inline std::string getName() override {
- return "MyMod";
- };
-
- auto getMethods() -> std::vector<CxxModule::Method> override;
+ void multiply(Runtime &rt, double a, double b, Function callback) override;
};
}
-extern "C" CxxModule* createMyModCxxModule();
-
#endif /* MYMOD_H */
- 修改react-native-my-mod.cpp
#include "react-native-my-mod.h"
-#include <cxxreact/JsArgumentHelpers.h>
-
-using namespace facebook::xplat;
namespace mymod {
-std::vector<CxxModule::Method> MyModCxxModule::getMethods() {
- return {
- CxxModule::Method(
- "multiply",
- [](folly::dynamic args, Callback cb) {
- cb({jsArgAsDouble(args, 0) * jsArgAsDouble(args, 1)});
- }),
- };
-}
+MyModCxxModule::MyModCxxModule(
+ std::shared_ptr<CallInvoker> jsInvoker)
+ : NativeMyModCxxSpecJSI(jsInvoker) {}
+void MyModCxxModule::multiply(Runtime &rt, double a, double b, Function callback) {
+ callback.call(rt, a * b);
}
-extern "C" CxxModule* createMyModCxxModule() {
- return new mymod::MyModCxxModule();
}
适配Android
- 修改MyModulePackage.java
package com.mymod;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.List;
// Dummy package for pure C++ NativeModules
public class MyModPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) {
return Collections.emptyList();
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactApplicationContext) {
return Collections.emptyList();
}
}
- 删除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);
- }
-}
- 修改build.gradle
buildFeatures {
prefab true
+ prefabPublishing true
+ }
+
+ prefab {
+ cpp {
+ headers "../cpp"
+ }
}
buildTypes {
- 修改CMakeLists.txt
include(Android-prebuilt.cmake)
+add_subdirectory(build/generated/source/codegen/jni)
+target_link_libraries(react_codegen_RNMyModSpec common_flags)
+
add_library(${CMAKE_PROJECT_NAME}
SHARED
../cpp/react-native-my-mod.cpp
- cpp-adapter.cpp
)
# Specifies a path to native header files.
@@ -21,11 +23,7 @@ target_include_directories(${CMAKE_PROJECT_NAME}
)
target_link_libraries(${CMAKE_PROJECT_NAME}
- fbjni
- folly_runtime
- glog
- react_nativemodule_core
- common_flags
+ react_codegen_RNMyModSpec
)
- 删除cpp-adapter.cpp
- 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/app的build.gradle
}
+ externalNativeBuild {
+ cmake {
+ path "$projectDir/src/main/jni/CMakeLists.txt"
+ }
+ }
signingConfigs {
- 添加CMakeLists.txt到example/android/app/src/main/jni
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# This CMake file is the default used by apps and is placed inside react-native
# to encapsulate it from user space (so you won't need to touch C++/Cmake code at all on Android).
#
# If you wish to customize it (because you want to manually link a C++ library or pass a custom
# compilation flag) you can:
#
# 1. Copy this CMake file inside the `android/app/src/main/jni` folder of your project
# 2. Copy the OnLoad.cpp (in this same folder) file inside the same folder as above.
# 3. Extend your `android/app/build.gradle` as follows
#
# android {
# // Other config here...
# externalNativeBuild {
# cmake {
# path "src/main/jni/CMakeLists.txt"
# }
# }
# }
cmake_minimum_required(VERSION 3.13)
# Define the library name here.
project(appmodules)
# This file includes all the necessary to let you build your application with the New Architecture.
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
find_package(react-native-my-mod REQUIRED CONFIG)
target_link_libraries(${CMAKE_PROJECT_NAME}
react-native-my-mod::cpp
)
- 添加OnLoad.cpp到example/android/app/src/main/jni
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// This C++ file is part of the default configuration used by apps and is placed
// inside react-native to encapsulate it from user space (so you won't need to
// touch C++/Cmake code at all on Android).
//
// If you wish to customize it (because you want to manually link a C++ library
// or pass a custom compilation flag) you can:
//
// 1. Copy this CMake file inside the `android/app/src/main/jni` folder of your
// project
// 2. Copy the OnLoad.cpp (in this same folder) file inside the same folder as
// above.
// 3. Extend your `android/app/build.gradle` as follows
//
// android {
// // Other config here...
// externalNativeBuild {
// cmake {
// path "src/main/jni/CMakeLists.txt"
// }
// }
// }
#include <DefaultComponentsRegistry.h>
#include <DefaultTurboModuleManagerDelegate.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <rncli.h>
#include <react-native-my-mod.h>
namespace facebook {
namespace react {
void registerComponents(
std::shared_ptr<ComponentDescriptorProviderRegistry const> registry) {
// Custom Fabric Components go here. You can register custom
// components coming from your App or from 3rd party libraries here.
//
// providerRegistry->add(concreteComponentDescriptorProvider<
// AocViewerComponentDescriptor>());
// By default we just use the components autolinked by RN CLI
rncli_registerProviders(registry);
}
std::shared_ptr<TurboModule> cxxModuleProvider(
const std::string &name,
const std::shared_ptr<CallInvoker> &jsInvoker) {
if (name == "MyMod") {
return std::make_shared<mymod::MyModCxxModule>(jsInvoker);
}
// Not implemented yet: provide pure-C++ NativeModules here.
return nullptr;
}
std::shared_ptr<TurboModule> javaModuleProvider(
const std::string &name,
const JavaTurboModule::InitParams ¶ms) {
// Here you can provide your own module provider for TurboModules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a library called `samplelibrary`):
//
// auto module = samplelibrary_ModuleProvider(moduleName, params);
// if (module != nullptr) {
// return module;
// }
// return rncore_ModuleProvider(moduleName, params);
// By default we just use the module providers autolinked by RN CLI
return rncli_ModuleProvider(name, params);
}
} // namespace react
} // namespace facebook
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider =
&facebook::react::cxxModuleProvider;
facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
&facebook::react::javaModuleProvider;
facebook::react::DefaultComponentsRegistry::
registerComponentDescriptorsFromEntryPoint =
&facebook::react::registerComponents;
});
}
适配iOS
- 修改MyMod.h
#import "react-native-my-mod.h"
#endif
-#import <React/RCTCxxModule.h>
-#import <ReactCommon/RCTTurboModule.h>
+#import <React/RCTBridgeModule.h>
-@interface MyMod : RCTCxxModule <RCTTurboModule>
-
-- (std::unique_ptr<facebook::xplat::module::CxxModule>)createModule;
+// Dummy RCTBridgeModule for pure C++ NativeModules
+@interface MyMod : NSObject <RCTBridgeModule>
@end
- 修改MyMod.mm
@implementation MyMod
RCT_EXPORT_MODULE()
-- (std::unique_ptr<facebook::xplat::module::CxxModule>)createModule {
- return std::make_unique<mymod::MyModCxxModule>();
-}
-
-- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
- return nullptr;
-}
-
@end
- 修改example/ios/MyModeExample的AppDelegate.mm
#import <React/RCTBundleURLProvider.h>
+#import <ReactCommon/RCTTurboModuleManager.h>
+
+#import <react-native-my-mod.h>
+
+@interface AppDelegate () <RCTTurboModuleManagerDelegate>
+@end
@implementation AppDelegate
@@ -23,4 +29,16 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
#endif
}
+#pragma mark RCTTurboModuleManagerDelegate
+
+- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
+ jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
+{
+ if (name == "MyMod") {
+ return std::make_shared<mymod::MyModCxxModule>(jsInvoker);
+ }
+
+ return nullptr;
+}
+
@end
测试适配结果
- Android运行my-mod
$ yarn example start &
$ yarn example android
- iOS运行my-mod
$ yarn example start &
$ yarn example ios
JS/C++的类型/方法转换
void SampleTurboCxxModule::voidFunc(jsi::Runtime &rt) {
// Nothing to do
}
bool SampleTurboCxxModule::getBool(jsi::Runtime &rt, bool arg) {
return arg;
}
double SampleTurboCxxModule::getEnum(jsi::Runtime &rt, double arg) {
return arg;
}
double SampleTurboCxxModule::getNumber(jsi::Runtime &rt, double arg) {
return arg;
}
jsi::String SampleTurboCxxModule::getString(
jsi::Runtime &rt,
const jsi::String &arg) {
return jsi::String::createFromUtf8(rt, arg.utf8(rt));
}
jsi::Array SampleTurboCxxModule::getArray(
jsi::Runtime &rt,
const jsi::Array &arg) {
return deepCopyJSIArray(rt, arg);
}
jsi::Object SampleTurboCxxModule::getObject(
jsi::Runtime &rt,
const jsi::Object &arg) {
return deepCopyJSIObject(rt, arg);
}
jsi::Object SampleTurboCxxModule::getValue(
jsi::Runtime &rt,
double x,
const jsi::String &y,
const jsi::Object &z) {
// Note: return type isn't type-safe.
jsi::Object result(rt);
result.setProperty(rt, "x", jsi::Value(x));
result.setProperty(rt, "y", jsi::String::createFromUtf8(rt, y.utf8(rt)));
result.setProperty(rt, "z", deepCopyJSIObject(rt, z));
return result;
}
void SampleTurboCxxModule::getValueWithCallback(
jsi::Runtime &rt,
const jsi::Function &callback) {
callback.call(rt, jsi::String::createFromUtf8(rt, "value from callback!"));
}
jsi::Value SampleTurboCxxModule::getValueWithPromise(
jsi::Runtime &rt,
bool error) {
return createPromiseAsJSIValue(
rt, [error](jsi::Runtime &rt2, std::shared_ptr<Promise> promise) {
if (error) {
promise->reject("intentional promise rejection");
} else {
promise->resolve(jsi::String::createFromUtf8(rt2, "result!"));
}
});
}