[ReactNative笔记]my-mod适配pure TurboCxxModule

144 阅读4分钟

准备环境

参考[ReactNative笔记]my-mod适配legecy TurboCxxModule

创建my-mod

参考[ReactNative笔记]my-mod适配legecy TurboCxxModule

origin-nojsi.png

基于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 &params) {
  // 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!"));
        }
      });
}