Android 进阶解密笔记-JNI 基础

JNI是JavaNativeInterface的缩写,用于Java与其他语言通信,常见于Android的音视频开发、热修复等场景。Android的NDK提供了JNI支持。JNI方法签名解决方法重载问题,局部引用在函数返回后会被自动释放,全局引用需手动释放且可跨线程,弱全局引用允许GC回收。
摘要由CSDN通过智能技术生成

在这里插入图片描述

JNI 是什么

JNIJava Native Interface的缩写,

译为本地接口,是Java与其他语言通信的桥梁。主要用于音视频开发、热修复和插件化、逆向开发,系统源码调用等

为了更方便使用JNI技术,Android提供了NDK工具集合。

Android系统按语言来分为Java世界与Native世界。由于很多程序与库都是native语言(比如C、C++等)编写,所以重复利用这些库很重要,这两个世界通过JNI来调用。

Native 注册

Native注册分为静态与动态注册

静态注册将Java方法和JNI函数建立关联,有一些缺点:

  1. JNI函数名称过长
  2. 声明Native方法类需要用javah生成头文件
  3. 初次调用Native方法需要建立关联,影响效率

JNI数据类型

  1. 基本数据类型转换处理void,其他数据类型只需在前面加上"j",如jbyte、jchar
  2. 引用数据类型转换:数组JNI层数据类型需要以"Array"结尾,签名格式开头都会有"[“,有些数据类型签名会以”;"结尾**

JNI方法签名

JNI方法签名格式为:(参数签名格式…)返回值签名格式**

方法签名: 为什么要方法签名,是为了解决 方法重载问题,当两个方法相同时,但参数不同,通过jni层调用同名方法时,通过方法签名来判断;

  1. 基本类型: boolean ->Z,byte-> B,char -> C,short-> S,int->I,long->J,float-> F,double->D,void -> V
  2. 如果是类的类型:L+类全名,类名中的.用/代替,比如java.lang.String就是Ljava/lang/String
  3. 如果是数组类型:则在前面加上然后加类型签名,几位数组就加几个,比如int[]->[I,boolean[][]->[[Z,java.lang.Class[] -> [Ljava/lang/Class

也可以通过javap -s来打印该方法的签名

Example:

public static native String getName();
public static native int calculate(int a,int b);

通过Rebuild Project才会在app中的intermediates目录下javac/debug 生成class文件,找到 类到地址 然后右键打开命令行

JNIEnvnative世界中Java环境的代表,通过JNIEnv *指针可以在native世界中访问Java世界代码操作,它只在创建它的线程有效,不能跨线程传递,因此不同的是独立的

作用:

  1. 调用Java方法
  2. 操作Java(操作Java中的变量和对象等)

局部引用、全局引用与弱引用比较

Java引用类型一样,JNI也有引用类型,分别是,本地引用、全局引用与弱引用

局部引用

局部引用:通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。会阻止GC回收所引用的对象,不在本地函数中跨函数使用,不能跨线前使用。函数返回后局部引用所引用的对象会被JVM自动释放,或调用DeleteLocalRef释放。(*env)->DeleteLocalRef(env,local_ref)

jclass cls_string = (*env)->FindClass(env, "java/lang/String");
 
jcharArray charArr = (*env)->NewCharArray(env, len);
 
jstring str_obj = (*env)->NewObject(env, cls_string, cid_string, elemArray);
 
jstring str_obj_local_ref = (*env)->NewLocalRef(env,str_obj); // 通过NewLocalRef函数创建
  1. 本地引用也就是局部引用:JNIEnv提供函数所返回的引用基本上都是本地引用

特点:

  1. native函数返回,它就会自动释放
  2. 只能在创建它的线程有效,不能够跨线程使用
  3. 局部引用是JVM负责的引用类型,受JVM管理

Android 中局部引用表最大数量为512个,如果超过这个表最大容量限制,就会造成局部引用表溢出,程序崩溃,所以不需要用局部引用时候要调用DeleteLocalRef立即删除。

JNI提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRefJNI规范指出,任何实现JNI规范的JVM,必须确保每个本地函数至少可以创建16个局部引用(可以理解为虚拟机默认支持创建16个局部引用)。

局部引用不能跨线程使用,只在创建它的线程有效。不要试图在一个线程中创建局部引用并存储到全局引用中,然后在另外一个线程中使用。

全局引用

全局引用:只能调用NewGlobalRef基于局部引用创建,会阻GC回收所引用的对象。可以跨方法、跨线程使用。JVM不会自动释放,必须调用DeleteGlobalRef手动释放`(env)->DeleteGlobalRef(env,g_cls_string);*

基于全局引用创建一个局引用返回,也同样会阻止GC回收所引用的这个对象,因为它们指向的是同一个对象

static jclass g_cls_string;
 
void TestFunc(JNIEnv *env, jobject obj) {
 
jclass cls_string = (*env)->FindClass(env, "java/lang/String");
 
g_cls_string = (*env)->NewGlobalRef(env,cls_string);
 
}
 
演示全局引用创建:
 
JNIEXPORT jstring JNICALL Java_com_study_jnilearn_AccessCache_newString
 
(JNIEnv *env, jobject obj, jcharArray j_char_arr, jint len)
 
{
 
    // ...
 
jstring jstr = NULL;
 
static jclass cls_string = NULL;
 
if (cls_string == NULL) {
 
jclass local_cls_string = (*env)->FindClass(env, "java/lang/String");
 
if (cls_string == NULL) {
 
return NULL;
 
}
 
// 将java.lang.String类的Class引用缓存到全局引用当中
 
cls_string = (*env)->NewGlobalRef(env, local_cls_string);
// 删除局部引用
 
        (*env)->DeleteLocalRef(env, local_cls_string);
        // 再次验证全局引用是否创建成功
 
        if (cls_string == NULL) {
 
        return NULL;
 
    }
 
}
// ....
return jstr;
}

特点:

  1. 不会自动释放,需要手动释放,不能被GC回收
  2. 可以跨线程使用
  3. 不受JVM管理
  4. 弱引用与全局引用类型不同,它可以被GC回收

弱全局引用

弱全局引用:调用NewWeakGlobalRef基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在JVM认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)

static jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
class cls_string = (*env)->FindClass(env, "java/lang/String");
g_cls_string = (*env)->NewWeakGlobalRef(env,cls_string);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值