Android NDK开发系列教程5:局部引用,全局引用,弱全局引用

简介: 终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~1. 简介从Java虚拟机创建的对象当传入到native层时会产生一个引用,在进行垃圾回收时如果有native的引用,改对象同样也不会被回收。

终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~

1. 简介

从Java虚拟机创建的对象当传入到native层时会产生一个引用,在进行垃圾回收时如果有native的引用,改对象同样也不会被回收。在native引用中分局部引用和全局引用。

1.1 局部引用

局部引用又称本地引用,大多数见到的引用都是局部引用,例如通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等),局部引用只会在本次native调用中有效,当本次调用结束后该引用即被自动释放。局部引用会阻止GC进行回收。同时也可以调用DeleteLocalRef函数来手动释放(比如在循环里面用到了局部引用而退出循环没有使用该局部引用,那么就需要在循环中释放该局部引用)。通常使用NewObject创建的实例返回的也是局部引用。千万不要把局部引用保存为c++的全局变量或者把它定义为静态变量,局部引用的有效期是一次Java本地调用。
JNI提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。

    //申请扩充局部引用的最大个数限制,返回值等于0的时候表示成功,>0时表示内存溢出。默认至少16个局部引用可以使用,引用数超出时报FatalError
    jint (*EnsureLocalCapacity)(JNIEnv*, jint);

    /* PushLocalFrame是一个创建本地引用新作用域的有用函数,这使得PushLocalFrame函数可以释放其使用的框架中所有已分配的本地引用。当该函数被调用时,本地引用的最低数量将在本框架中被创建。该函数如果执行成功则返回0,如果由于错误抛出一个OutOfMemoryException,则返回一个负值。*/   
    jint PushLocalFrame(jint capacity);

    /*PopLocalFrame函数释放当前框架中的所有本地引用(弹出一个框架)。因为存储该函数的结果(返回值)可能会导致在即将被弹出的框架中创建一个本地引用,该函数接收一个可以导致引用在当前框架被弹出之后的最高框架中创建的参数。这就确保可以维护一个存储PopLocalFrame函数结果的引用。*/
    jobject PopLocalFrame(jobject result);

PushLocalFrame为当前函数中局部引用创建了一个引用堆栈,在每遍历一次调用(*env)->GetObjectArrayElement(env, arr, i);返回一个局部引用时,JVM会自动将该引用压入当前局部引用栈中。而PopLocalFrame负责将栈中所有引用释放。这样一来,Push/PopLocalFrame函数对提供了对局部引用生命周期更方便的管理,不用再去一个个Delete了。

1.2 全局引用

全局引用可以在当前线程使用,也可以在其他线程使用,可以保存在本地的static静态变量或全局变量中,全局引用需要调用NewGlobalRel函数创建,释放时采用ReleaseGlobalRef函数释放。有效作用域在创建后,一直到调用ReleaseGlobalRef释放时。

1.3 弱全局引用

在Java1.2中,新增了弱全局引用,与全局变量一样其创建、删除均需要编程写出,也可以在本地多个代码中使用,也可以跨进程使用。不一样的是,它的存在不影响垃圾回收机制对该引用所指向对象实例的回收。其创建采用NewWeakGlobalRef,释放采用ReleaseWeakGlobalRel。

以上涉及的函数主要有以下几个:

//创建局部引用
jobject NewLocalRef(jobject obj);
//释放局部引用
void DeleteLocalRef(jobject obj);
//创建全局引用
jobject NewGlobalRef(jobject obj);
//释放全局引用
void DeleteGobalRef(jobject obj);
//创建弱全局引用
jobject NewWeakGlobalRef(jobject obj);
//释放弱全局引用
void DeleteWeakGlobalRef(jobject obj);
//该方法判断两个引用是否相等,对于弱全局引用如果对比的是NULL那么还可以判断该引用指向的对象是否被回收
jboolean IsSameObject(jobject obj1 , jobject obj2);

上述三中引用会影响内存的回收,在C/C++中没有向Java一样的垃圾回收机制,自己申请的内存要记得自己去释放了,否则会导致内存泄漏。虽然现在C/C++里面也有智能指针,但相对而言这个智能指针用起来不如Java。所以在C的世界里要遵循谁申请,谁释放的基本原则。

2. 举个栗子

上面介绍了基本知识,下面给出相应的例子来进行说明下。

2.1局部引用

    //1. 局部引用不要存储在static变量中,即使存了下次也不能用
    //static jclass cls;
    //以下创建的局部引用都放入到栈中
    env->PushLocalFrame(16);
    jclass cls;
    if (!cls) {//这里就错误了,前一次方法完成后jvm会释放局部引用,这里static存的值仅第一次有效
        cls = env->GetObjectClass(instance);//这里的cls是局部引用
    }

    //删除栈里面的局部引用
    env->PopLocalFrame(NULL);
    env->EnsureLocalCapacity(20);//将本地引用的最大限制改为20
    //下面可以进行其他操作。。。

在局部引用中要注意以下几方面:
1. 循环体内创建的局部引用,要在循环体内就直接释放了。
2. 编写的工具函数,里面创建的局部引用,要在该工具函数里面释放了。
3. 局部引用引用了一个大的Java对象,这时候一定一定要早点释放了。
4. 局部引用不要缓存在native层

2.2 全局引用

extern "C"
JNIEXPORT void JNICALL
Java_zqc_com_example_NativeTest_jniGlobalRef(JNIEnv *env, jobject instance) {
    static jobject obj;
    static jclass pCls;
    if (obj) {//第二次点击时,这里就不会空
        //由于obj和personCls被保存为全局引用了,所有这里使用仍然有效
        jmethodID getId = env->GetMethodID(pCls, "getName", "()Ljava/lang/String;");
        jstring name = (jstring) env->CallObjectMethod(obj, getId);
        LOGE("obj is not null, name:%s", jstringToChar(env, name));
        return;
    }
    if (!pCls) {//为空就去新建
        jclass tmpCls = env->FindClass("zqc/com/example/Person");
        pCls = (jclass) env->NewGlobalRef(tmpCls);
        env->DeleteLocalRef(tmpCls);
    }
    jmethodID conMid = env->GetMethodID(pCls, "<init>", "()V");
    jobject tmpObj = env->NewObject(pCls, conMid);
    jmethodID setId = env->GetMethodID(pCls, "setName", "(Ljava/lang/String;)V");
    env->CallVoidMethod(tmpObj, setId, env->NewStringUTF("看看姓名"));
    obj = env->NewGlobalRef(tmpObj);
    env->DeleteLocalRef(tmpObj);
}

2.3 弱全局引用

弱全局引用和全局引用基本差不多,最大的区别就是弱全局引用不影响GC的回收。在使用弱全局引用的时候一定要注意,使用前要检查下是不是被GC回收了。

extern "C"
JNIEXPORT void JNICALL
Java_zqc_com_example_NativeTest_jniWeakGlobalRef(JNIEnv *env, jobject instance) {
    static jclass pCls;
    if (!pCls) {
        jclass tmpCls = env->FindClass("zqc/com/example/Person");
        pCls = (jclass) env->NewWeakGlobalRef(tmpCls);
        env->DeleteLocalRef(tmpCls);
    }
    //除了第一次需要FindClass外,在没有回收pCls之前都可以使用

    //这里使用...

    //可以手动释放
    //env->DeleteWeakGlobalRef(pCls);
}

3. 引用的比较

jni提供了相应的函数

jboolean IsSameObject(jobject ref1, jobject ref2)
{ return functions->IsSameObject(this, ref1, ref2); }

如果两个引用指向同一个实例则返回JNI_TRUE,否则返回JNI_FALSE。

目录
相关文章
|
1月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
1月前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
27 1
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
20天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
41 19
|
1月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
20天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
46 14
|
23天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
21天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
31 5
|
20天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
21天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。