Android学习笔记1-4--机制1--JNI

来源:互联网 发布:淘宝活动时间表 编辑:程序博客网 时间:2024/06/11 09:10

JNI简介

JNI:Java Native Interface。即Java函数和Native函数通过该接口可以互相调用。JNI层必须实现为动态库的形式,这样Java虚拟机才能加在并调用其的函数。
1) Java层对应类名为无要求,关键点是通过手动写代码加载进来的JNI库。
2) JNI层对应库名在不同底层平台下名称不一样,如linux下libXXX_jni.so和Windows下XXX_jni.dll。
3) Native层对应库名为去掉JNI库名的_jni而形成的名称。

JNI使用

Native函数使用分两大步:在Java层JNI库的加载和相关Native函数声明;在JNI层Native函数注册。
1.1) 在Java层JNI库的加载:

加载时机和加载地点为任何时候、任何地方均可,通行做法是在类起始的static语句块中加载。加载形式System.loadLibrary(“XXX_jni”)。系统会自动根据不同底层平台扩展成真实的JNI动态库文件名。

1.2) 在Java层Native函数声明:

Native函数为抽象函数。在函数名返回值前加上Native即可,有final要放在final前。

2) 在JNI层Native函数注册:

2.1) 静态注册
当Java层函数调用Native函数时,虚拟机会查找JNI库中对应静态注册JNI函数:Java函数找到对应的JNI函数是通过包名.类名.函数名的字符串转换(.转为_)而得到的。找不到就会报错;找到了就保存JNI层函数的函数指针供本次和以后使用。这种方法需要Java工具javah的参与,流程如下:

a) 编写java代码形成.class文件。
b) 使用javah命令javah xxx.class -o yyy,会输出yyy.h的JNI层头文件(yyy的命名格式一般为包名_类名_class(.转为_),其声明了JNI层函数),再对其头文件实现JNI层对应的Native函数。

2.2) 动态注册
当Java层加载JNI库完成时,虚拟机会调用JNI库中JNI_OnLoad函数开始动态注册JNI函数:JNI技术中叫JNINativeMethod的结构用来记录Java、Native函数与JNI函数一一对应的关系。AndroidRunTime类提供了注册函数工具registerNativeMethods操作该结构完成注册。虚拟机通过调用注册函数加载器JNI_OnLoad开始注册。

// JNINativeMethod结构定义typedef struct{    // Java中Native函数的名字,不用携带包和类名    const char* name;    // Java中Native函数的签名,用字符串表示,是参数和返回值的组合    const char* signature;    // JNI层对应函数的函数指针,他的类型为void*类型    void* fnPtr;}
// registerNativeMethods函数定义int registerNativeMethods(JNIEnv* env,const char* className,const JNINativeMethod* gMethods,int numMethods){    // 调用JNI层的jniRegisterNativeMethods函数    return jniRegisterNativeMethods(env,className,gMethods,numMethods);}// jniRegisterNativeMethods函数定义简介int jniRegisterNativeMethods(JNIEnv* env,const char* className,const JNINativeMethod* gMethods,int numMethods){    // 获取具体的类名。因JNINativeMethod中name非全路径名,多以要找寻是那个类    jclass clazz = (*env)->FindClass(env,className);    ...    // 调用JNIEnv的RegisterNatives函数注册关联关系    if((*env)->RegisterNatives(env,clazz,gMethods, numMethods)<0){        return -1;    }    return 0;}
// JNI_OnLoad函数例子jint JNI_OnLoad(JavaVM* vm,void* reserved){    // ...}

创建cpp文件内容结构如下:
a) 在里面定义个关于Java路径类的宏(指定要注册的类)以及定义个JNINativeMethod数组(指定要注册的函数)。
b) 实现JNI层对应的Native函数,编写注册函数包装器JNI_RegisterNativeMethods以及实现注册函数加载器JNI_OnLoad。

// NDEBUG宏是标准C中定义的宏(专门用来控制assert()的行为)// 如果定义了这个宏则assert不会起作用。要在<assert.h>之前不然禁用assert不会成功#define NDEBUG#include <assert.h>#include <stdlib.h>#include <jni.h>// 定义关于Java路径类的宏(指定要注册的类)#define JNIREG_CLASS "cn/test/andr/test/service/TestJNIImp"// 定义JNINativeMethod数组(指定要注册的函数)static JNINativeMethod JNIREG_METHODS[] = {     {             "getCPPString",             "()Ljava/lang/String;",             (void *) jni_getCPPString     }};// JNI层对应的Native函数JNIEXPORT jstring JNICALL jni_getCPPString(JNIEnv *env, jclass clazz) {     return (*env)->NewStringUTF(env, "hello world");}// 注册函数包装器JNI_RegisterNativeMethodsstatic int JNI_RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods, int numMethods) {     jclass clazz = (*env)->FindClass(env, className);     if (clazz == NULL) {         return JNI_FALSE;     }     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {         return JNI_FALSE;     }     return JNI_TRUE;}// 注册函数加载器JNI_OnLoadjint JNI_OnLoad(JavaVM *vm, void *reserved) {     JNIEnv *env = NULL;     jint JNI_VERSION = JNI_VERSION_1_4;     if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION) != JNI_OK) {         return -1;     }     assert(env != NULL);     if (!JNI_RegisterNativeMethods(env, JNIREG_CLASS, JNIREG_METHODS, sizeof(JNIREG_METHODS) / sizeof(JNIREG_METHODS[0]))) {//注册         return -1;     }     return JNI_VERSION;}

JNI深入

类型转换和类型签名

暂时省略

引用类型和垃圾回收

暂时省略

JNIEnv和JavaVM

暂时省略

JNI的异常处理逻辑

暂时省略


一个简单的demo