目录
前言
之前初略的学习了一下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()进行判断。
异常函数的使用,可以看《》