一,JNI与NDK的关系
JNI(Java Native Interface),JAVA本地接口,是java标准平台的一部分,通过使用 Java本地接口编写程序,可以确保代码在不同的平台上方便移植。
JNI提供了将Java与C/C++、汇编等本地代码集成的方案,Android中最常用的就是c和c++:用C语言编写代码生成一个库文件;然后在java中调用这个库文件中的函数;
NDK(Native Development Kit),原生开发工具包,是一系列的开发工具。
NDK可以帮助开发者快速开发C/C+的动态库,自动将so和java应用一起打包成apk。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
二,静态注册使用方法
1,java类中声明native方法
static native int native_method1(int arg1);
static native int native_method2(byte arg1[],long arg2,int arg3);
2,用javah命令生成.h头文件
javah com.smm.testjni.TestJni(生成头文件类的全路径,包名+类名)
3,编写c/cpp代码,实现具体方法
#include <string.h>
#include <jni.h>
#include "com_okwap_testjni.h"
JNIEXPORT jint JNICALL Java_com_smm_testjni_TestJni_native_method1(JNIEnv* env, jclass, jint arg1){
...
...
return 123;
}
4,编写mk文件编译
三,动态注册方法
入口函数JNI_OnLoad()
当JavaVM执行到System.loadLibrary(xxx)函数时,首先会去执行JNI组件中的JNI_OnLoad()函数,而当VM释放该组件时会呼叫JNI_OnUnload()函数,JNI_OnLoad()代码示例如下:
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *e;
int status;
if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6)) {
return JNI_ERR;
}
if ((status = android::register_com_android_framework_coretests_JNITests(e)) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
有两个参数,其中*jvm
为Java虚拟机实例,JavaVM结构体定义了以下函数:
DestroyJavaVM
AttachCurrentThread
DetachCurrentThread
GetEnv
我们可以看到上面JNI_OnLoad()的函数的代码中第四行有
jvm->GetEnv((void **) &env
这里通过调用了GetEnv函数获取JNIEnv结构体指针(JNI环境变量),其实JNIEnv结构体是指向一个函数表的,该函数表指向了一个对应的JNI函数,我们通过调用这些JNI函数实现JNI编程。
RegisterNatives函数完成动态注册
上面我们获取了JNIEnv变结构体指针,之后我们便可以通过结构体指针里的RegisterNatives函数去完成动态注册native方法。
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
第一个参数clazz是Java层对应包含native方法的对象,这里就是TestJni对象了,通过JNIEnv获取。
jclass clazz = env->FindClass("com/smm/testjni/TestJni");
第二个参数methods是JNINativeMethod结构体指针,这里 JNINative结构体是描述Java层Native方法的,定义如下:
typedef struct {
const char* name;//Java层native方法的名字
const char* signature;//Java层native方法的描述符
void* fnPtr;//对应JNI函数的指针
} JNINativeMethod;
第三个参数为注册native方法的数量,一般会动态注册多个native方法,首先会定义一个JNINativeMethod数组,然后将该指针作为RegisterNative函数的参数传入
static JNINativeMethod methods[] = {
{"native_method1","(I)I",(void*)native_method1},
{"native_method2","([BJI)I",(void*)native_method2}
};
最后调用RegisterNative函数完成动态注册:
env->RegisterNatives(clz, methods, sizeof(methods)/sizeof(method[0]));
JNIEnv结构体
JNIEnv结构体指向一个函数表,该函数表指向一系列的JNI函数,我们通过调用这些JNI函数可以与Java层进行交互,以下是常用的函数:
//获取Java对象中某个变量的ID
jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
//根据变量的ID获取布尔域
jboolean GetBooleanField(jobject obj, jfieldID fieldID)
//获取Java对象中对应的方法ID
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
//根据methodID调用对应对象中的方法,并且该方法的返回值Void类型
CallVoidMethod(jobject obj, jmethodID methodID, ...)
//根据methodID调用对应对象中的方法,并且该方法的返回值Boolean类型
CallBooleanMethod(jobject obj, jmethodID methodID, ...)
JNI数据类型
-
原始数据类型
-
引用类型
jobject (all Java objects)
jclass (java.lang.Class objects)
jstring (java.lang.String objects)
jarray (array)
jobjectArray (object arrays)
jbooleanArray (boolean arrays)
jbyteArray (byte arrays)
jcharArray (char arrays)
jshortArray (short arrays)
jintArray (int arrays)
jlongArray (long arrays)
jfloatArray (float arrays)
jdoubleArray (double arrays) -
方法和变量的ID
当需要调用Java中的某个方法的时候我们首先要获取它的ID,根据ID调用JNI函数获取该方法,变量的获取过程也是同样的过程,这些ID的结构体定义如下:
struct _jfieldID; /* opaque structure */
typedef struct _jfieldID *jfieldID; /* field IDs */
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */
网友评论