JNI学习手册

Android  NDK  2023年8月10日 am8:08发布1年前 (2023)更新 城堡大人
148 0 0

前言

之前初略的学习了一下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异常的使用

参考文章

 历史上的今天

  1. 2022: [代码片段]ListView+Letter排序代码(0条评论)
  2. 2019: 李娟: 通往一家人去的路(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

Kotlin之子类和子类型

前言简单记录下一下Kotlin中子类和子类型的相关知识,方便自己查阅。小结子类是继承的概念,如果B继承A,则B就是A的子类。如果需要使用类型A的变量时,可以使用类型B的变量来代替,则此时类型B就是A的子类型。在需要使用时,用子类去替代父类,此时子类就是父类的子类型。正文...

WordPress评论提交速度很慢的原因和解决办法

最近使用Nana主题时,发现提交比较卡,因此百度了一下,发现大家也是一样的。由于大神也遇到,因此他们都解决了此问题,因此我也摘抄于此,以便查阅。一般分为两个问题(大多数吧),一是WordPress设置问题,二是服务器问题。1、WordPress设置问题这个比较简单,你只需要在 WordPre...

陈先发:前世

要逃,就干脆逃到蝴蝶的体内去不必再咬着牙,打翻父母的阴谋和药汁不必等到血都吐尽了。要为敌,就干脆与整个人类为敌。他哗地一下脱掉了蘸墨的青袍脱掉了一层皮脱掉了内心朝飞暮倦的长亭短亭。脱掉了云和水这情节确实令人震悚:他如此轻易地又脱掉了自己的骨头!我无限眷恋的最后一幕是:他们纵身一跃...

Binder个人简单总结

前言Binder对于Android开发,很熟悉,也很陌生。我也经常用,但对其原理不是很了解,因此参考其他大佬的,做一下笔记。简单记录一下Android中Binder的原理(其实主要摘抄),方便自己回顾。正文什么是BinderBinder是Android中的一种跨进程通信(IPC)的方式。...

Settings中开发者模式的影藏和显示

涉及目录:/SettingsMainActivity/src/com/android/settings/SettingsActivity.java/SettingsMainActivity/src/com/android/settings/DeviceInfoSettings.java...

《MySQL基础教程》笔记1

前言学习一下MySQL,之前一直没有系统的学习一下。最近有空,看了《MySQL基础教程-西泽梦路》,简单的做一下笔记。记录于此,方便自己回忆。正文我这以Window版的phpstudy软件验证。需要进入这个目录,才可以使用mysql命令D:\phpstudy_pro\Extension...