JNI是Java Native Interface的缩写(Java本地调用)。
Java平台和本地C代码进行相互操作的API接口称为Java本地代码接口。
Java之所以不能和C/C++想通,最主要的原因就是类型差异。那么该什么场景使用JNI?
1.当应用程序需要访问系统的各个特性和设备,并且这些特性和设备通过Java平台是无法访问时。
2.在一些要求效率和速度的场合,本地代码的运行效率高,比如复杂数学运算,游戏上的3D效果等等。 需要Native语言实现时。
3.当某个模块功能,已经有了一个成熟的,且经过大量测试的N ative代码实现,并且知道如何将其移植到所使用的目标平台上时。
注册之意就是将Java层的native函数与JNI层对应的实现函数关联起来,这样在调用java层的native函数时,就能顺利转到JNI层对应的函数执行。
注册分静态注册和动态注册两种方式。
所谓的静态注册,就是直接在Java文件调用native库以及写个native方法 然后再c/c++文件中实现这个方法就行了!如:
test.java
static {
System.loadLibrary("droidrobot");
}
public static native int getapkversion(String packagename);
test.cpp
jint Java_com_lib_droidrobot_getapkversion(JNIEnv *env,jobject thiz, jstring packagename)
{
.....
return 1;
}
动态注册好处是不要写很长的方法名
什么时候被调用注册?
Java层调用System.loadLibrary加载完JNI库后,紧接着会查找叫jni_onload的函数,如果有就调用它,而动态注册工作就在这里完成。
所以要实现动态注册,必须实现重写jni_onload方法。虽然静态方法没这个要求,但也建议实现一些,里面可以做一些初始化工作。
例子:
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
......
if (NULL != env && registerNativeMethods(env) == 0) {
result = JNI_VERSION_1_4;
}
return result; //必须返回此值,否则报错
}
*registerNativeMethods:*
static int registerNativeMethods(JNIEnv* env) {
//找到对应的类
jclass clazz = env->FindClass("android/drm/DrmManagerClient");
//把类和native的方法结合关联起来,所有方法都在nativeMethods数组中定义了。
if (env->RegisterNatives(clazz, nativeMethods, sizeof(nativeMethods)![这里写图片描述](https://img-blog.csdn.net/20180413095539935?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2tyaXNfZmVp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
/ sizeof(nativeMethods[0])) == JNI_OK) {
result = 0;
}
return result;
}
Java调用native函数,就需要通过一个位于JNI层的动态库来实现,这个通常是在类的static语句中加载,调用System.loadLibrary方法,该方法的参数是动态库的名称。
如:
static {
System.loadLibrary("droidrobot");
}
先看两个基本概念:
JNIEnv代表Java环境。通过这个JNIEnv*指针,就可以对Java端的代码进行操作。如创建Java类对象,调用Java对象的方法,获取Java对象的属性等。
JNIEnv是一个指向了一个Pointer,这个Pointer指向了一张函数表,这张函数表中的每一项就是JNI中的一个函数的入口。本地的方法通过查找这张表来调用某个jni函数,来和JVM交互。
JNIEnv 与 JavaVM :
– JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个;
– JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个 JNIEnv;
JNIEnv 作用 :
– 调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码;
– 操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象;
为了能够在C/C++使用Java类,jni.h头文件中专门定义了jclass类型来表示Java中的Class类
jclass获取:
JNIEnv类中有如下几个简单的函数可以取得jclass
jclass FindClass(const char* clsName) 根据类名来查找一个类,完整类名。
jclass GetObjectClass(jobject obj) 根据一个对象,获取该对象的类
jclass GetSuperClass(jclass obj) 获取一个类的父类
FindClass 会在classpath系统环境变量下寻找类,需要传入完整的类名,注意包与包之间是用”/”而不是”.”来分割
如:
jclass cls_string= env->FindClass("java/lang/String");
jclass作用:
因为JNI中使用的方法名并非全路径名,所以要指明是哪个类。
比如调用类的静态方法,静态属性就需要通过这个方法来获取一个类。
有了类和对象之后,用以下方法访问:
JNI在jni.h头文件中定义了jfieldID,jmethodID类表示Java端的属性和方法
1. GetFieldID/GetMethodID //获取属性
2. GetStaticFieldID/GetStaticMethodID //获取方法
JNIEnv调用Java方法:
1. (*jniEnv)->CallStaticObjectMethod(jniEnv, TestProvider, getTime); //静态调用
2. (*jniEnv)->CallVoidMethod(jniEnv, mTestProvider, sayHello,jstrMSG); //非静态调用
AndroidJNI 通过C++调用JAVA
JNI静态注册之Java调C一
android NDK开发 静态/动态注册 jni
JNI学习之步步深入一
深入理解JNI
Android JNI入门第六篇——C调用Java
《深入理解Android:卷一》