导航号,我的单页导航
文章目录

前言

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

正文

JNI之数据类型

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

具体请看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得到本地方法的声明,然后再通过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之函数介绍三之字符串操作部分简单的常用的函数。

函数参数介绍
# 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中对应的函数只有两个默认的参数,这两个默认参数也是很重要的。

  1. 第一个参数

    是JNIEnv * env指针。

  2. 第二个参数

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

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

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

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

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

访问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之引用

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配合,但对于复杂的,这个就不行了。

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之对象数组使用

JNI之异常

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

  1. 检查返回值是否为NULL

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

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

参考文章

© 版权声明
导航号,我的单页导航

暂无评论

暂无评论...