Android JNI 调用流程

devtools/2024/9/25 6:08:24/

为啥要用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 libraryandroidlog)

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

//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_SOSHAREDIMPORTED )//指定头文件位置
include_directories(${CMAKE_CURRENT_SOURCE_DIR})//目标库
set_target_properties(DRIVER_SOPROPERTIES 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_SOandroidlog)

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

三 、调用方式

上面的都是编译语法,下面才是我们的核心逻辑,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.ppmy.cn/devtools/116836.html

相关文章

网络编程的应用

目录 1.单机程序和网络程序 2.客户端与服务端 3.网络编程三要素 3.1 IP地址 3.2 port端口 4.TCP编程 5.UDP编程 1.单机程序和网络程序 之前编写的程序都是单机程序&#xff0c;所有的业务功能实现及数据存储都在一个主机上完成&#xff0c;我们称为单机程序 我们在生活…

Android数据序列化总结

Android数据序列化总结 什么是序列化 序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间&#xff0c;对象将其当前状态写入到临时或持久性存储区。以后&#xff0c;可以通过从存储区中读取或反序列化对象的状态&#xff0c;重新创建该对…

【C++】Eclipse技巧汇总

Eclipse C/C调试无法输入 在debug C/C程序时&#xff0c;Eclipse自带的窗口&#xff0c;无法读取cin等输入 解决办法&#xff1a; 参考&#xff1a;https://blog.csdn.net/sagjhdj/article/details/123271383 思路是调用外部console&#xff1a; 依次点击Debug>Debug Conf…

【计算机视觉】YoloV8-训练与测试教程

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 制作数据集 Labelme 数据集 数据集选用自己标注的&#xff0c;可参考以下&#xff1a…

美团中间件C++一面-面经总结

1、TCP和UDP 的区别&#xff1f; 速记标识符&#xff1a;连靠刘墉宿营 解释&#xff1a; 面向连接vs无连接 可靠传输vs不保证可靠 字节流vs报文传输 拥塞控制流量控制vs无 速度慢vs速度快 应用场景自己描述 2、服务端处于close wait是什么情况&#xff0c;是由什么造成的&…

俯卧撑动作起伏识别计数系统源码分享

俯卧撑动作起伏识别计数检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Co…

armbian debian 系统安装overlayroot后无法启用

千盼万盼 终于debian12可以用了 它终于也跟ubuntu 系统一样可以安装overlayroot了 但是 满怀欣喜的装完了发现 压根没法启动 这。。。。。 原因吗 也简单。。。 默认的映像里没有busybox......... 而它有这个要求。。。 overlayroot 包中有一个小错误&#xff1a;它要求 gr…

深度学习:数据增强

目录 前言 一、为什么要使用数据增强&#xff1f; 二、数据增强有哪些方法&#xff1f; 1. 几何变换 2. 颜色变换 3. 噪声添加 4. 裁剪 5. 混合技术 6. 其他方法 三、代码实现 前言 数据增强是深度学习中常用的一种技术&#xff0c;旨在通过对训练数据进行各种变换来…