ReactNative调用jni的so技术分享

ReactNative调用jni的so技术分享

一.前言

​ 项目内容需要与同事进行协同开发,主要为安卓方面,考虑到后续有混合开发的需求,本人在RN和Flutter进行相关测试。简单说下两种框架在此次项目上的区别。

1.Flutter,项目上如果使用 技术方向是Dart驱动java代码再使用jni的so库.(参考RN调用的思路)

优势:期间测试发现调用普通so库方面和C/C++源码非常方便(ps:本身自带的diff的方法非常的好用。),

总而言之很强大,功能使用上复杂度比RN要方便很多,文件架构清晰,后起之秀!

Android 上使用 dart:ffi 调用本地代码 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

缺点:需要学会dart和了解flutter的框架,学习周期成本比较高 (无奈放弃=w=)

2.ReactNavtive,项目上如果使用 技术方向是Js使用NativeModule驱动java代码再使用jni的so库.

Android 原生模块 · React Native 中文网

优势:熟悉React和JS的使用降低学习成本....

缺点:在c/c++调取方面 官网上说明使用新的框架,相关配置比flutter复杂很多...

简单总结下:flutter不愧是后起之秀,感觉很多方面使用下来都比前辈RN上方便许多。希望RN之后使用新的框架能够改善此类问题。

二.项目上使用总结

项目总体思路:Js 使用 nativeModule 在调用java代码 使用jni的so库

1.安装RN的环境

参考官方文档,注意一点新的0.72版本,java8用不了,最低11

React Native 需要 Java Development Kit [JDK] 11。你可以在命令行中输入 javac -version(请注意是 javac,不是 java)来查看你当前安装的 JDK 版本。如果版本不合要求,则可以去TemurinOracle JDK上下载(后者下载需注册登录)。

低于 0.67 版本的 React Native 需要 JDK 1.8 版本(官方也称 8 版本)。

搭建开发环境 · React Native 中文网

2.配置nativeModule

首先贴一个官方模板,我是参考这个的原生模块配置 · React Native 中文网

java 复制代码
npx @react-native-community/bob create react-native-awesome-module

Android 原生模块 · React Native 中文网

按照官网所要求的指定目录:android/app/src/main/java/com/model/ModuleFuncManager.java

​ 此java 意义则是模块功能管理

(1)其中getName()这个方法等于是 告知RN你这里的模块名字是啥 我这里是MyModule,记住方法复写 @Override

(2)@ReactMethod则是RN可调用的方法,isBlockingSynchronousMethod = true代表同步调用,下面就可以写被调用的逻辑

这里我使用的是调取JNI的so库 并调用它的stringFromJNI()方法,当然输入值也是收到啥输出啥

注意!!

import com.example.mylib.sdklib; 是调取jni库的函数,这里要使用sdklib类所以这样引入!

java 复制代码
package com.testdemo;

import android.widget.Toast;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import com.example.mylib.sdklib;


public class ModuleFuncManager extends ReactContextBaseJavaModule  {
    private static ReactApplicationContext reactContext;

    public ModuleFuncManager(ReactApplicationContext context) {
        super(context);
        reactContext = context;
    }

    @Override
    public String getName() {
        return "MyModule";
    }

    //@ReactMethod
    @ReactMethod(isBlockingSynchronousMethod = true)
    public String myMethod(String message) {
        // 在这里编写你的Java代码逻辑
        // 例如,可以打印消息到日志
        //System.out.println("Received message: " + message);
        //Toast.makeText(getReactApplicationContext(), message).myMethod();
       // promise.resolve(message);
       //System.out.println("Received message: " + message);
       sdklib sdk=new sdklib();
        String a=sdk.stringFromJNI();

    // // //return message+a;
     System.out.println("Received message: " + a);
    return message+a;
    }
}

此java 意义注册模块的包

基本上此处参考官网的案例写的

java 复制代码
package com.testdemo;

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;
import java.util.ArrayList;

public class ModulePackage implements ReactPackage {
    
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new ModuleFuncManager(reactContext));
        
        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

此java主要由模板脚手架生成,主要是把 注册-java包添加到reactapp里

packages.add(new ModulePackage()); 这是修改的内容

后面都是模板所写,项目上用不到

java 复制代码
package com.testdemo;

import android.app.Application;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactNativeHost;
import com.facebook.soloader.SoLoader;
import java.util.List;




public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost =
      new DefaultReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          // Packages that cannot be autolinked yet can be added manually here, for example:
          // packages.add(new MyReactNativePackage());
          packages.add(new ModulePackage());
          //new ModeulePackage();
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }

        @Override
        protected boolean isNewArchEnabled() {
          return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
        }

        @Override
        protected Boolean isHermesEnabled() {
          return BuildConfig.IS_HERMES_ENABLED;
        }
      };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      // If you opted-in for the New Architecture, we load the native entry point for this app.
      DefaultNewArchitectureEntryPoint.load();
    }
    ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }
}

3.使用jni的库注意事项

这里有大坑!!!!请仔细阅读可解决读不到so和读取报错等问题!!!

这里使用的jni方法,注意C++那边的命名方式,c++那边命名为Java_com_example_mylib_sdklib_stringFromJNI。所以这里package包是com.example.mylib,这样jni就能读取正确!

java 复制代码
package com.example.mylib;

public class sdklib {
    static{
        System.loadLibrary("mylib");
    }
    public native String stringFromJNI();
}
 

下面的步骤是我在androidStudio上调用jni遇到的坑,所以到RN上的安卓模块可能也会遇到!

\android\app\build.gradle

这部分我是从androidStudio直接拷贝过来的, 这里sourceSets指向也请务必写进去

css 复制代码
android {
	.....
 sourceSets {
        main {
            jniLibs.srcDirs = ['jniLibs']
        }
    }
}

dependencies {
.....
  implementation 'androidx.appcompat:appcompat:1.6.1'

  implementation 'com.google.android.material:material:1.8.0'

  testImplementation 'junit:junit:4.13.2'

  androidTestImplementation 'androidx.test.ext:junit:1.1.5'

  androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
  .....
  }

注意\android\app\jniLibs 请创建 并放入jni生成各个框架so文件

当然最关键的部分还是build之后 so库的获取,这个jnilib创建,并指向到了指定目录

会在自动在android\app\build\intermediates\merged_jni_libs\debug\out 生成框架so文件,如果没有手动放入也是可以的!!!

可能会发生重构clean的操作,可以在build.gradle 写一个搬运代码

举个例子:

typescript 复制代码
task copyTask(type: Copy) {
    from '.jnilib/x86_64/mylib.so'
    into '../build/app/intermediates/merged_jni_libs/debug/out/lib/x86_64'
}

可以解决找不到so的问题,关键就是这路径里没有框架so库导致!!


4.在主程部分正常使用NativeModules

来到ts这块,声明了一个同步和异步的方法,用的是同步,这里MyModule 就是 ModuleFuncManager.java 声明的名字

typescript 复制代码
import { NativeModules } from 'react-native';

type ModuleType = {
  myMethod(a:string): Promise<string>;
};
type ModuleType1 = {
  myMethod(a:string): string;
};

const { MyModule } = NativeModules;

export default MyModule as ModuleType1;

接下来就是正常调用

javascript 复制代码
type SectionProps = PropsWithChildren<{
  title: string;
}>;

function Section({children, title}: SectionProps): JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  return (
    <View style={styles.sectionContainer}>
      <Text
        style={[
          styles.sectionTitle,
          {
            color: isDarkMode ? Colors.white : Colors.black,
          },
        ]}>
        {title}
      </Text>
      <Text
        style={[
          styles.sectionDescription,
          {
            color: isDarkMode ? Colors.light : Colors.dark,
          },
        ]}>
        {children}
      </Text>
    </View>
  );
}

function App() {

 const TextValue =MyModule.myMethod("你好,hello Java!");
 return (
    <>
			.....
          <Section title="Step One">
            Edit <Text style={styles.highlight}>App1.tsx</Text> to change this
            screen and then come back to see your edits.你好{TextValue}
          </Section>
     </>

命令行运行 yarn android,真机调试如下所示:

三.总结

希望让遇到同样问题的人能够少走一些弯路!就是我分享的动力=w=

相关推荐
烬奇小云4 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野16 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO16 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他16 小时前
Android ANR分析总结
android
PenguinLetsGo18 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博21 小时前
音频格式转换
android·音视频
音视频牛哥1 天前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.1 天前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农1 天前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android