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=

相关推荐
大白要努力!13 分钟前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee19 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood31 分钟前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-3 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen6 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年13 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿16 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神17 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛17 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法17 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526