JNI
1、创建 native 方法
public static native String getStringFromNDK()
2、进入class 目录 执行 javah -jni 生成 .h 头文件
3、创建 cpp 文件,导入头文件,实现对应的方法
JNIEXPORT jstring JNICALL Java_gebilaolitou_ndkdemo_NDKTools_getStringFromNDK
(JNIEnv *env, jobject obj)
4、导入 so 库
static {
System.loadLibrary("ndkdemotest-jni");
}
5、CMake配置
gralde 配置
externalNativeBuild {
cmake {
cppFlags ""
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
add_library( 创建动态库
find_library( 发现三方库
target_link_libraries 自己的 so和三方库关联
kotlin native 方法
external fun getSecretKey(): String
object JniHelper {
init {
try{
System.loadLibrary("native-lib")
}catch (e: Exception){
e.printStackTrace()
}
}
external fun getSecretKey(): String
}
生成.h 头文件
切换到项目全路径 username/SmartKT/app/build/tmp/kotlin-classes/debug kotlin生成 class 文件目录
javah -d username/SmartKT/app/src/main/cpp/ com.aaron.aaronkotlin.JniHelper
在 目录 username/SmartKT/app/src/main/cpp/ 下生成.h 头文件
CMakeLists.txt
# cmake最低版本号
cmake_minimum_required(VERSION 3.18.1)
# add_library:把一个library添加到工程
add_library(
native-lib
SHARED
native-lib.cpp)
# 找到预编译库 log_lib 并link到我们的动态库 native-lib中
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# 首个参数是target,后面的参数是item;target必须先用add_library()创建过;
target_link_libraries(
native-lib
${log-lib} )
#include <jni.h>
#include <android/log.h>
#define TAG "JNI_LOG"
#define AndroidLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#ifndef _Included_com_hzbank_aaronkotlin_JniHelper
#define _Included_com_hzbank_aaronkotlin_JniHelper
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_hzbank_aaronkotlin_JniHelper_getSecretKey
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include "native-lib.h"
extern "C"
JNIEXPORT jstring JNICALL Java_com_hzbank_aaronkotlin_JniHelper_getSecretKey
(JNIEnv *env, jobject ){
char* app_key = "5465465416948";
AndroidLOGD("执行getSecretKey获取密钥");
return env->NewStringUTF(app_key);
}
jboolean boolean 布尔值
jbyte byte 8位字符
jchar char 16位字符
jshort short 2 字节
jint int 4字节
jlong long 8字节
jfloat float 4字节
jdouble double 8
void void
jobject Object java 对象
jclass class Class 对象
jstring String 字符串
jobjectArray Object[]
jbooleanArray boolean[]
jbyteArray char[]
jshortArray short[]
jintArray int[]
jlongArray long[]
jdoubleArray double[]
jthrowable Throwable
JNIEnv
JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JVM中可能创建多个Java线程,每个线程对应一个JNIEnv结构,它们保存在线程本地存储TLS中。因此,不同的线程的JNIEnv是不同,也不能相互共享使用。JNIEnv结构也是一个函数表,在本地代码中通过JNIEnv的函数表来操作Java数据或者调用Java方法。也就是说,只要在本地代码中拿到了JNIEnv结构,就可以在本地代码中调用Java代码。
调用Java 函数:JNIEnv代表了Java执行环境,能够使用JNIEnv调用Java中的代码
操作Java代码:Java对象传入JNI层就是jobject对象,需要使用JNIEnv来操作这个Java对象
JNIEnv的创建
:_JavaVM是C++中JavaVM结构体,调用jint AttachCurrentThread(JNIEnv** p_env, void* thr_args) 方法,能够获取JNIEnv结构体;
JNIEnv的释放
调用JavaVM结构体_JavaVM中的jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); } 方法,就可以释放 本线程的JNIEnv
JNIEnv与线程
JNIEnv是线程相关的,即在每一个线程中都有一个JNIEnv指针,每个JNIEnv都是线程专有的,其他线程不能使用本线程中的JNIEnv,即线程A不能调用线程B的JNIEnv。所以JNIEnv不能跨线程。
JNIEnv只在当前线程有效:JNIEnv仅仅在当前线程有效,JNIEnv不能在线程之间进行传递,在同一个线程中,多次调用JNI层方便,传入的JNIEnv是同样的
本地方法匹配多个JNIEnv:在Java层定义的本地方法,能够在不同的线程调用,因此能够接受不同的JNIEnv
JNIEnv相关的常用函数
创建Java中的对象
env->NewObject()
jclass myClass = env->FindClass("com/example/MyClass");
jmethodID constructor = env->GetMethodID(myClass, "<init>", "(I)V");
jobject newObject = env->NewObject(myClass, constructor, value);
env->NewObjectA()
env->NewObjectV()
创建Java类中的String对象
env->NewString()
env->NewStringUTF()
JNI的引用
Java内存管理这块是完全透明的,new一个实例时,只知道创建这个类的实例后,会返回这个实例的一个引用,然后拿着这个引用去访问它的成员(属性、方法),完全不用管JVM内部是怎么实现的,如何为新建的对象申请内存,使用完之后如何释放内存,只需要知道有个垃圾回收器在处理这些事情就行了,然而,从Java虚拟机创建的对象传到C/C++代码就会产生引用,根据Java的垃圾回收机制,只要有引用存在就不会触发该该引用所指向Java对象的垃圾回收。
局部引用(Local Reference)、全局引用(Global Reference)、弱全局引用(Weak Global Reference)
局部引用,也成本地引用,通常是在函数中创建并使用.在函数返回的时候自动释放(freed),也可以使用DeleteLocalRef函数手动释放该应用
全局引用可以跨方法、跨线程使用,直到被开发者显式释放。类似局部引用,一个全局引用在被释放前保证引用对象不被GC回收。和局部应用不同的是,没有俺么多函数能够创建全局引用。能创建全部引用的函数只有NewGlobalRef,而释放它需要使用ReleaseGlobalRef函数
弱全局引用(Weak Global Reference)
通过使用NewWeakGlobalRef、ReleaseWeakGlobalRef来产生和解除引用。
try{
System.loadLibrary("native-lib")
}catch (e: Exception){
e.printStackTrace()
}
让Java层的native方法和任意JNI函数连接起来
try{
System.loadLibrary("native-lib")
}catch (e: Exception){
e.printStackTrace()
}
System.loadLibarary()方法加载so库的时候,Java虚拟机就会找到这个JNI_OnLoad函数兵调用该函数,这个函数的作用是告诉Dalvik虚拟机此C库使用的是哪一个JNI版本
会调用JNI_OnUnload()函数来进行善后清除工作。
jint JNI_OnLoad(JavaVM* vm, void* reserved)
jniRegisterNativeMethods
JNI规范定义的函数签名信息
Z boolean
B byte
C char
S short
I int
J long
F float
D double
[i int[]
[Ljava/lang/Object String[]
Ljava/lang/String String
Ljava/lang/Object Object
如果返回值是void,对应的签名是V。
native代码反调用Java层代码
jclass myClass = env->FindClass("com/example/MyClass");
jmethodID constructor = env->GetMethodID(myClass, "<init>", "(I)V");
jobject newObject = env->NewObject(myClass, constructor, value);