前言

之前初略的学习了一下JNI的使用,也做了对应的笔记。为了方便自己复现,这里就把所有文章整理在一起,方便自己查询。

正文

JNI之数据类型

Java中调到Native方法传递的参数是Java类型,这些参数需要通过Dalvik虚拟机转换为JNI类型。

具体请看JNI之类型介绍

基本数据类型
  1. Java 类型     本地类型   说明
  2. boolean     jboolean   无符号, 8
  3. byte       jbyte       无符号, 8
  4. char       jchar       无符号, 16
  5. short       jshort     有符号, 16
  6. int         jint       有符号, 32
  7. long       jlong       有符号, 64
  8. float       jfloat     32
  9. double     jdouble     64
  10. void       void       N/A
复制
引用类型
  1. jobject                 (所有java对象)
  2. jclass                 (Class对象)
  3. jstring               (String对象)
  4. jarray                 (数组)
  5.   jobjectArray         (object[]数组)
  6.   jbooleanArray       (boolean[]数组)
  7.   jbyteArray           (byte[]数组)
  8.   jshortArray         (short[]数组)
  9.   jintArray           (int[]数组)
  10.   jlongArray           (long[]数组)
  11.   jfloatArray         (float[]数组)      
  12.   jdoubleArray         (double[]数组)
  13. jthrowable             (Throwable[]数组)
复制
类型签名
  1. 类型签名   Java类型
  2. Z           boolean
  3. B           byte
  4. C           char
  5. S           short
  6. I           int
  7. J           long
  8. F           float
  9. D           double
  10. L           fully-qualified-class ; 全限定的类
  11. [           type type[]
  12. (           arg-types ) ret-type 方法类型
复制

JNI之注册

JNI注册分为静态注册动态注册,对于初学,先学习静态注册,对JNI有一定了解,再学习动态注册。

静态注册

先由Java得到本地方法的声明,然后再通过JNI实现该声明方法。

  1. 优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低

  2. 缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高

推荐看JNI静态注册

动态注册

动态注册java的Native方法,使得c/c++方法名可以和java的Native方法名可以不同。动态注册是将二者方法名关联起来,以后在修改Native方法名时,只需修改动态注册关联的方法名称即可。

  1. 优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高

  2. 缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

推荐看JNI动态注册,JNI动态注册封装C语言版,之前用C语言,后面发现C++比较方便,就转为C++了,可以看JNI动态注册封装C++版

JNI之函数

JNI函数是在jni.h中定义的,jni.h(NDK中jni.h头文件完整内容)这里有所有的函数定,一般推荐先看几个常用的,熟悉后在进一步的深入其他函数。很多函数都是类似的,可以举一反三学习。

具体看JNI之函数介绍一JNI之函数介绍二JNI之函数介绍三之字符串操作部分简单的常用的函数。

函数参数介绍
  1. # Java中
  2. public static native String hello();
  3. public native String hello2();
  4. # JNI中
  5. # 静态hello()
  6. JNIEXPORT jstring JNICALL Java_com_biumall_dynamic_one_Hello_hello
  7. (JNIEnv *, jclass);
  8. # 非静态hello2()
  9. JNIEXPORT jstring JNICALL Java_com_biumall_dynamic_one_Hello_hello2
  10. (JNIEnv *, jobject);
复制

上面java中定义是没有传入参数的,因此JNI中对应的函数只有两个默认的参数,这两个默认参数也是很重要的。

  1. 第一个参数

    是JNIEnv * env指针。

  2. 第二个参数

    这个根据本地方法[java中native方法]是否有static而定。

    如果是静态方法,比如上面的hello(),此时参数二是【类的引用】,也就是jclass。(重点)

    如果是非静态方法,比如上面的hello2(),此时参数二是【对象的引用】,也就是jobject。(重点)

PS: 对应第二个参数是jclass还是jobject,是很重要的,函数调用中会涉及。

推荐看JNI之函数的参数介绍

访问Java方法

JNI允许本地方法访问Java对象的域和调用方法。这里简单的复现一下访问Java方法。

  1. # Hello.java中
  2. # 非静态
  3. public int add(int x, int y) {
  4.   return x + y;
  5. }
  6. # 非静态
  7. public int sub(int x, int y) {
  8.   return x - y;
  9. }
  10. # 静态
  11. public static int multiply(int x, int y) {
  12.   return x * y;
  13. }
复制

对于静态和非静态jmethodID的获取方法存在不一,JNI中有区分的。

非静态
  1. # 获取jmethodID
  2. jmethodID addMethodID = env->GetMethodID(clazz, "add", "(II)I");
  3. jmethodID subMethodID = env->GetMethodID(clazz, "sub", "(II)I");
  4. # 调用Java非静态方法
  5. if(NULL != addMethodID){
  6.   count = env->CallIntMethod(object, addMethodID, x, y);
  7. }
  8. if(NULL != subMethodID){
  9.   count = env->CallIntMethod(object, subMethodID, x, y);
  10. }
复制
静态
  1. # 获取jmethodID
  2. jmethodID multiplyMethodId = env->GetStaticMethodID(clazz, "multiply", "(II)I");
  3. # 调用Java静态方法
  4. if(NULL == multiplyMethodId){
  5.   count = env->CallStaticIntMethod(clazz, multiplyMethodId, x, y);
  6. }
复制

细节这里不介绍,可以看JNI之访问方法和域

JNI之引用

JNI中定义了了三种引用。

局部引用

作用范围是本线程,生命周期为一次Native调用。

局部引用包括多数JNI函数的创建引用,Native方法返回值和参数。

局部引用只有在它的Native方法的线程中有效,该方法返回后,会被虚拟机回收。

如果不调用DeleteLocalRef ,局部引用在函数返回后会被回收(不是马上被回收);如果调用DeleteLocalRef ,局部引用会立即被回收

全局引用

全局引用(Global Reference)可以跨方法、跨线程使用,直到它被手动释放才会失效。同局部引用一样,全局引用也会阻止它所引用的对象被 GC 回收。

全局引用是通过JNI函数NewGlobalRef创建,必须通过DeleteGlobalRef释放,否则永远不会被回收。

弱全局引用

弱全局引用 (Weak Global Reference),简称弱引用。弱引用使用 NewGlobalWeakRef 创建,使用 DeleteGlobalWeakRef 释放。

与全局引用类似,弱引用可以跨方法、线程使用。与全局引用不同的是,弱引用不会阻止 GC 回收它所指向的 VM 内部的对象。

由于弱引用会被回收,所以在使用前最好通过IsSameObject进行判断是否回收了。

JNI之数组

至于数组相关操作,推荐看下面一系列吧。

JNI之函数介绍四之数组操作

JNI之数组简单操作

JNI之List集合

JIN之自定义对象

前面涉及到类型签名,对于基本数据类型签名,可以通过javac和javac配合,但对于复杂的,这个就不行了。

  1. L           fully-qualified-class ; 全限定的类
复制

举个例子

  1. # 自定义Staff类
  2. package com.biumall.dynamic.one;
  3. public class Staff {
  4. //略
  5. }
复制
  1. public static native void native_printfStaff(Staff staff);
  2. //传入Staff通过jmethodID获取值
  3. public static native void native_printfStaff2(Staff staff);
  4. //从JNI中创建Staff
  5. public static native Staff native_createStaff();
复制

上面三个方法的类型签名

  1. # native_printfStaff(Staff staff)
  2. (Lcom/biumall/dynamic/one/Staff;)V
  3. # native_printfStaff2(Staff staff)
  4. (Lcom/biumall/dynamic/one/Staff;)V
  5. # native_createStaff()
  6. ()Lcom/biumall/dynamic/one/Staff;
复制

哈哈,具体的请看下面的JNI之自定义对象使用,这里还涉及数组的JNI之对象数组使用

JNI之异常

JNI函数在执行过程中会出现异常,其异常处理机制与Java不一。JNI中提供两种检查异常的方法。

  1. 检查返回值是否为NULL

  2. 通过JNI的异常函数ExceptionOccurred()进行判断。

异常函数的使用,可以看JNI异常的使用

参考文章

相关文章

暂无评论

none
暂无评论...