JNI之引用简介

NDK开发2023年7月28日 am8:08发布1年前 (2023)更新 3XCN.COM站长
0 0 0
广告也精彩
目录

前言

简单的记录一下JNI的局部引用全局引用弱全局引用,这对于写程序还是很有帮助的。

正文

深入了解是,先看看JNI中引用的重点知识。

  1. JNI 支持三种引用:局部引用、全局引用、弱全局引用(简称:弱引用)。

  2. 局部引用和全局引用有不同的生命周期。当本地方法返回时,局部引用会被自动释放。而全局引用和弱引用必须手动释放。

  3. 局部引用或者全局引用会阻止 GC 回收它们所引用的对象,而弱引用则不会 。

  4. 不是所有的引用可以被用在所有的场合。例如,一个本地方法创建一个局部引用并返回后,再对这个局部引用进行访问是非法的。

局部引用

局部引用(Local Reference)最常见的引用类型。

释放一个局部引用有两种方式,一个是本地函数执行完后VM自动释放,另外一个是通过DeleteLocalRef手动释放。

既然局部引用自动释放,那为什么还需手动释放?

如果不调用DeleteLocalRef ,局部引用在函数返回后会被回收;如果调用DeleteLocalRef ,局部引用会立即被回收。两种方式最后都会释放,但立即释放(调用DeleteLocalRef )可一定的缓解内存少的问题。

这个感觉跟Java中的GC原理有点类似,对象不用了,不一定马上会被回收,需要等到一定的条件才会回收。(个人理解)

全局引用

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

全局引用需要通过NewGlobalRef()函数进行创建!

jstring MyNewString(JNIEnv *env, jchar *chars, jint len){
    static jclass stringClass = NULL;
    ...
    if (stringClass == NULL) {
        jclass localRefCls =(*env)->FindClass(env, "java/lang/String");
        if (localRefCls == NULL) {
            return NULL; 
        }
        //创建全局引用
        stringClass = (*env)->NewGlobalRef(env, localRefCls);
        //删除局本引用
        (*env)->DeleteLocalRef(env, localRefCls);
        //判读全局引用是否创建成功
        if (stringClass == NULL) {
            return NULL;
        }
    }
    ...
}

码中通过FindClass返回的局部引用localRefCls,然后在通过NewGlobalRef创建String类的全局引用。删除localRefCls后,再检查NewGlobalRef是否成功。

与局部引用不同,全局引用的创建不是由 JNI 自动创建的,全局引用需要调用 NewGlobalRef 函数,而释放它需要使用 ReleaseGlobalRef 函数。

下面是全局引用创建和删除函数,以及局部引用的删除函数。

#全局引用
jobject     (*NewGlobalRef)(JNIEnv*, jobject);
void        (*DeleteGlobalRef)(JNIEnv*, jobject);
#局部引用
void        (*DeleteLocalRef)(JNIEnv*, jobject);

弱全局引用

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

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

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

缓存变量

既然介绍了,总得试试身手吧。在JNI中为了获取java属性和方法,会先获取jfieldID与jmethodID,然后进行查询属性值或调用java方法。如果将jfieldID与jmethodID缓存起来,调用时不必每次都查询。

一般缓存有如下两种方式:

  1. 使用时缓存

  2. 初始化时缓存

使用时缓存

使用时缓存,也即是第一次调用是就保存下来,等下次再次调用时直接使用缓存的值即可。

JNIEXPORT jint JNICALL native_jni_computer(JNIEnv *env, jobject object, jstring type, jint x, jint y{
    int count = -1;
    jclass clazz = clazz = env->GetObjectClass(object);
    //根据type进行加减乘
    const char *charType = env->GetStringUTFChars(type, 0);
    if (!strcmp(charType, "-")) {
        //静态变量,只会初始化一次
        static jmethodID subMethodID = NULL;
        if(NULL == subMethodID){
            subMethodID = env->GetMethodID(clazz, "sub", "(II)I");
        }
        if(NULL != subMethodID){
            count = env->CallIntMethod(object, subMethodID, x, y);
        }
    } else if (!strcmp(charType, "+")) {
        //静态变量
        static jmethodID addMethodID = NULL;
        if(NULL == addMethodID ){
            addMethodID = env->GetMethodID(clazz, "add", "(II)I");
        }
        if(NULL != addMethodID){
            count = env->CallIntMethod(object, addMethodID, x, y);
        }
    } else if (!strcmp(charType, "*")) {
        //静态变量
        static jmethodID multiplyMethodId = NULL;
        if(NULL == multiplyMethodId ){
            multiplyMethodId = env->GetStaticMethodID(clazz, "multiply", "(II)I");
        }
        if(NULL != multiplyMethodId){
            count = env->CallStaticIntMethod(clazz, multiplyMethodId, x, y);
        }
    }
    env->ReleaseStringUTFChars(type, charType);
    return count;
}
初始化时缓存

Android Framework中就很多用这种方式,也就是提前init。

static jfieldID mBooleanFieldID;
static jfieldID mIntFieldID;
static jmethodID mSubMethodID;
static jmethodID mAddMethodID;

JNIEXPORT void JNICALL native_jni_init(JNIEnv *env, jobject obj){
    jclass clazz = env->FindClass(DYNAMIC_CLASS);
    mjobject = env->NewWeakGlobalRef(obj);
    mBooleanFieldID = env->GetFieldID(clazz, "mBooleanValue", "Z");
    mIntFieldID = env->GetFieldID(clazz, "mIntValue", "I");
    mSubMethodID = env->GetMethodID(clazz, "sub", "(II)I");
    mAddMethodID = env->GetMethodID(clazz, "add", "(II)I");
}

参考文章

  1. 《JNI编程指南中文版》

  2. 《JNI完全手册》

  3. JNI:全局引用&局部引用&弱全局引用

  4. 《深入理解Android卷1(邓凡平)》

 历史上的今天

版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站文章: 部分来源于网络,仅供站长学习和参考,若侵权请留言
广告也精彩

相关文章

广告也精彩

暂无评论

评论审核已启用。您的评论可能需要一段时间后才能被显示。

暂无评论...

网站升级中

公告

近期网站升级中,可能存在一些bug。欢迎反馈 https://www.91es.com/we.html

本站域名

本站域名 : 91es.com3xcn.com。本站邮箱 : 站长邮箱 i@oorr.cn,通知邮箱we@oorr.cn ,如有更新,请看公告 。