目录
前言
之前初略的学习了一下JNI的使用,也做了对应的笔记。为了方便自己复现,这里就把所有文章整理在一起,方便自己查询。
正文
JNI之数据类型
Java中调到Native方法传递的参数是Java类型,这些参数需要通过Dalvik虚拟机转换为JNI类型。
具体请看《》
基本数据类型
Java 类型 本地类型 说明 boolean jboolean 无符号, 8 位 byte jbyte 无符号, 8 位 char jchar 无符号, 16 位 short jshort 有符号, 16 位 int jint 有符号, 32 位 long jlong 有符号, 64 位 float jfloat 32 位 double jdouble 64 位 void void N/A
引用类型
jobject (所有java对象) jclass (Class对象) jstring (String对象) jarray (数组) jobjectArray (object[]数组) jbooleanArray (boolean[]数组) jbyteArray (byte[]数组) jshortArray (short[]数组) jintArray (int[]数组) jlongArray (long[]数组) jfloatArray (float[]数组) jdoubleArray (double[]数组) jthrowable (Throwable[]数组)
类型签名
类型签名 Java类型 Z boolean B byte C char S short I int J long F float D double L fully-qualified-class ; 全限定的类 [ type type[] ( arg-types ) ret-type 方法类型
JNI之注册
JNI注册分为静态注册和动态注册,对于初学,先学习静态注册,对JNI有一定了解,再学习动态注册。
静态注册
先由Java得到本地方法的声明,然后再通过实现该声明方法。
-
优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
-
推荐看《》
动态注册
动态注册java的Native方法,使得c/c++方法名可以和java的Native方法名可以不同。动态注册是将二者方法名关联起来,以后在修改Native方法名时,只需修改动态注册关联的方法名称即可。
-
优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
-
缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败
推荐看《》,《》,之前用C语言,后面发现C++比较方便,就转为C++了,可以看《》
JNI之函数
JNI函数是在jni.h中定义的,jni.h(《》)这里有所有的函数定,一般推荐先看几个常用的,熟悉后在进一步的深入其他函数。很多函数都是类似的,可以举一反三学习。
具体看《》,《》和《》部分简单的常用的函数。
函数参数介绍
# Java中 public static native String hello(); public native String hello2(); # JNI中 # 静态hello() JNIEXPORT jstring JNICALL Java_com_biumall_dynamic_one_Hello_hello (JNIEnv *, jclass); # 非静态hello2() JNIEXPORT jstring JNICALL Java_com_biumall_dynamic_one_Hello_hello2 (JNIEnv *, jobject);
上面java中定义是没有传入参数的,因此JNI中对应的函数只有两个默认的参数,这两个默认参数也是很重要的。
-
第一个参数
是JNIEnv * env指针。
-
第二个参数
这个根据本地方法[java中native方法]是否有static而定。
如果是静态方法,比如上面的hello(),此时参数二是【类的引用】,也就是jclass。(重点)
如果是非静态方法,比如上面的hello2(),此时参数二是【对象的引用】,也就是jobject。(重点)
PS: 对应第二个参数是jclass还是jobject,是很重要的,函数调用中会涉及。
推荐看《》。
访问Java方法
JNI允许本地方法访问Java对象的域和调用方法。这里简单的复现一下访问Java方法。
# Hello.java中 # 非静态 public int add(int x, int y) { return x + y; } # 非静态 public int sub(int x, int y) { return x - y; } # 静态 public static int multiply(int x, int y) { return x * y; }
对于静态和非静态jmethodID的获取方法存在不一,JNI中有区分的。
非静态
# 获取jmethodID jmethodID addMethodID = env->GetMethodID(clazz, "add", "(II)I"); jmethodID subMethodID = env->GetMethodID(clazz, "sub", "(II)I"); # 调用Java非静态方法 if(NULL != addMethodID){ count = env->CallIntMethod(object, addMethodID, x, y); } if(NULL != subMethodID){ count = env->CallIntMethod(object, subMethodID, x, y); }
静态
# 获取jmethodID jmethodID multiplyMethodId = env->GetStaticMethodID(clazz, "multiply", "(II)I"); # 调用Java静态方法 if(NULL == multiplyMethodId){ count = env->CallStaticIntMethod(clazz, multiplyMethodId, x, y); }
细节这里不介绍,可以看《》
JNI之引用
JNI中定义了了三种引用。
局部引用
作用范围是本线程,生命周期为一次Native调用。
局部引用包括多数JNI函数的创建引用,Native方法返回值和参数。
局部引用只有在它的Native方法的线程中有效,该方法返回后,会被虚拟机回收。
如果不调用DeleteLocalRef ,局部引用在函数返回后会被回收(不是马上被回收);如果调用DeleteLocalRef ,局部引用会立即被回收。
全局引用
全局引用(Global Reference)可以跨方法、跨线程使用,直到它被手动释放才会失效。同局部引用一样,全局引用也会阻止它所引用的对象被 GC 回收。
全局引用是通过JNI函数NewGlobalRef创建,必须通过DeleteGlobalRef释放,否则永远不会被回收。
弱全局引用
弱全局引用 (Weak Global Reference),简称弱引用。弱引用使用 NewGlobalWeakRef 创建,使用 DeleteGlobalWeakRef 释放。
与全局引用类似,弱引用可以跨方法、线程使用。与全局引用不同的是,弱引用不会阻止 GC 回收它所指向的 VM 内部的对象。
由于弱引用会被回收,所以在使用前最好通过IsSameObject进行判断是否回收了。
JNI之数组
至于数组相关操作,推荐看下面一系列吧。
《》
《》
《》
JIN之自定义对象
前面涉及到类型签名,对于基本数据类型签名,可以通过javac和javac配合,但对于复杂的,这个就不行了。
L fully-qualified-class ; 全限定的类
举个例子
# 自定义Staff类 package com.biumall.dynamic.one; public class Staff { //略 }
public static native void native_printfStaff(Staff staff); //传入Staff通过jmethodID获取值 public static native void native_printfStaff2(Staff staff); //从JNI中创建Staff public static native Staff native_createStaff();
上面三个方法的类型签名
# native_printfStaff(Staff staff) (Lcom/biumall/dynamic/one/Staff;)V # native_printfStaff2(Staff staff) (Lcom/biumall/dynamic/one/Staff;)V # native_createStaff() ()Lcom/biumall/dynamic/one/Staff;
哈哈,具体的请看下面的《》,这里还涉及数组的《》
JNI之异常
JNI函数在执行过程中会出现异常,其异常处理机制与Java不一。JNI中提供两种检查异常的方法。
-
检查返回值是否为NULL
-
通过JNI的异常函数ExceptionOccurred()进行判断。
异常函数的使用,可以看《》
参考文章