当前位置: 首页 > article >正文

Android JNI 调用流程

为啥要用JNI,我个人理解是,Java 代码效率不够高,代码调用底层逻辑隔着一层Java 虚拟机,不能直接操控底层硬件,而C/C++ 可以直接操控硬件设备,对于需要效率更高的操作,就需要通过C/C++ 完成。。

比如说我们公司做的一个项目,VR眼镜,连接主机设备,VR需要上报数据给主机,并且显示出来。眼镜转动很快,,如果数据获取慢,那主机显示很延时很久,所以这里java 就不合适了。

一、项目结构

先简单介绍下目录结构,如果想自己本地调试打开android studio   File->New->New Project  选择 native C++ 项目,系统会为我们创建一个demo 。

新建完成build.gradle会指定cmakelist,该文件会用于编译native 库,一般不建议更改

   externalNativeBuild {
        cmake {
            path = file("src/main/cpp/CMakeLists.txt")
            version = "3.22.1"
        }
    }

然后就是natIve 库的存放位置,系统会新增一个cpp 文件夹,不建议自己修改路径。

这些都是系统自动编译的,我们需要做的是加载native 库,然后调用就可以了

   static {
        System.loadLibrary("myapplication");
    }

 public native String stringFromJNI();

流程就是这样,下面介绍下具体实现。

二、CMakelist 参数意义

这个是常用的方式,

一,配置native 库


project("myapplication")


add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp)


target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        log)

系统自动生成几个字段,挨个解释下。

//native 库名称,可自己修改,java 端加载需要根据这个来。
project("myapplication")   

//这个简单来说就是库文件中,包含的代码。

add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp)


//简单来说就是native 库中包含哪些代码,如果有多个直接换行就可以了
add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp

        xxxxx.cpp

        yyyy.cpp

        )

也可以设置目录

aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}  SRC_LIST)
add_library(${CMAKE_PROJECT_NAME}  SHARED ${SRC_LIST})


//链接一些Android 三方 关联到改native 库中,比如说需要使用到一些android Log 库
target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        log)

二,链接第三方so

上面说的,都是加载自己的c++ 代码和Android 原生库,那么怎么加载三方so呢。

//设置三方库的路径
set(libs ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs)
//别名
add_library(DRIVER_SO
        SHARED
        IMPORTED )

//指定头文件位置
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

//目标库
set_target_properties(
        DRIVER_SO
        PROPERTIES IMPORTED_LOCATION
        ${libs}/xxxx.so

//查找原生库

find_library( # Sets the name of the path variable.
        android-lib
        # Specifies the name of the NDK library that
        # you want CMake to locate.
        android)

//链接到目标库

target_link_libraries( ${CMAKE_PROJECT_NAME}
        DRIVER_SO
        android
        log
        )

目前用到的就是这些,其他的使用时可以去查询。

三 、调用方式

上面的都是编译语法,下面才是我们的核心逻辑,demo 里面 也已经给了方式。

注意JNI 方法名 java_包名_类名_函数名 

Java_com_and_myapplication_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

首先需要注意JNI的类型 

如果回调 Java 方法,也很简单。

/**
    找到对应的class
*/
jclass mImuCallBackJ = jniEnv->FindClass(JAVA_CALL_BACK);
jclass strClass = jniEnv->FindClass("java/lang/String");

    jmethodID ctorID = jniEnv->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = jniEnv->NewByteArray(strlen(data));
    jniEnv->SetByteArrayRegion(bytes, 0, strlen(data), (jbyte*)data);
    jstring encoding = jniEnv->NewStringUTF("utf-8");
    jstring callData =  (jstring)jniEnv->NewObject(strClass, ctorID, bytes, encoding);
/**
    找到对应的method
*/
   
        jmethodID msendImuData = jniEnv->GetStaticMethodID(mImuCallBack, "onIMUEvent", "(Ljava/lang/String;)V");
/**
   调用函数
*/
    jniEnv->CallStaticVoidMethod(mImuCallBackJ, msendImuData,callData);

上面是基本用法,以下几点需要注意。

1,返回类型和参数,基本类型没啥好说的,主要是复合类型,最后的分号一定要记住“;”,不然会找不到对应的方法。

2,字符串 String类,找的是“java/lang/String”  而不是"Ljava/lang/String",这个是针对返回值和参数的,用错了一些莫名奇妙的问题就产生了。

jclass objClass = env->FindClass("java/lang/String");

3,so 如何调用java 

我们知道想调用java ,必须要通过JVM 。so库里面如何获取呢。

C++ 代码通过重写下面代码的方式获取JVM ,JNI 加载so 的时候,自动执行,有JVM 了,我们就可以正常回调java 方法 了

JavaVM* javaVM;
int jni_version = JNI_VERSION_1_4;

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
    LOG_D("JNI_OnLoad %d", load_count++);
    javaVM = vm;
    return JNI_VERSION_1_4;
}
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
    LOG_D("JNI_OnUnload %d", unload_count++);
}

参考

Android Jni GetMethodID中函数标识的简单解释-CSDN博客

Android-JNI开发系列《八》CMakeLists.txt语法&使用_jni cmakelist-CSDN博客


http://www.kler.cn/a/318812.html

相关文章:

  • 使用pdfjs加载多页pdf并实现打印
  • 网页版五子棋——对战模块(服务器端开发②)
  • 深度学习代码笔记
  • 鸿蒙进阶篇-属性动画-animateTo转场动画
  • 服务号消息折叠折射出的腾讯傲慢:上云会不会也一样?
  • JUC-locks锁
  • 深入理解主键回显:提升数据操作效率与准确性
  • 目标检测系列(一)什么是目标检测
  • OpenMV与STM32通信
  • 稳了,搭建Docker国内源图文教程
  • Mysql梳理10——使用SQL99实现7中JOIN操作
  • C++ 面试模拟02
  • 2024 年最新 Protobuf 结构化数据序列化和反序列化详细教程
  • React Native 在 build 的时候如果出现 `babel.config.js` 配置文件的错误
  • 音频北斗定位系统有什么用?
  • C++入门编程题(力扣):字符串中最多数目的子序列
  • EasyExcel日常使用总结
  • 【C语言】使用 OpenSSL 进行 AES CBC 加密与解密
  • linux静态路由表
  • Nginx静态资源优化、压缩、缓存处理
  • 像百度谷歌这种网站会被DDoS吗
  • 全球商旅,提升人效,就选用友BIP超级版!
  • 【果蔬识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
  • 基于springboot在线点餐系统
  • 2024源代码加密软件TOP10排行榜丨保护企业源代码安全不泄露
  • [数据结构与算法·C++版] 笔记 1.2 什么是数据结构