Android系统篇之----Android中的智能指针

一、前言

今天我们开启Android系统篇的文章了,其实一直想弄,只是之前一直没有太多深入的了解,最近又把这块拿出来好好看了一下,所以想从新梳理一下,来看看Android中的这块知识,首先我们今天来看一下:Android中的智能指针的概念,为什么说先看一下智能指针这个知识呢?因为我们在看Android源码的时候,会发现几乎好多地方都用到了这个东东,所以我们在介绍后面的知识点,先来看看这个吧。


二、问题

那么Android中的智能指针是个什么东西呢?我们知道Android用的Java语言开发的,Java语言是没有指针这个概念的,当然Java有JNI技术,C/C++有指针的概念的,那么为什么叫做智能呢?智能指针到底是不是真的指针,如何做到智能,我们带着疑惑去看看这些问题。

其实Android中的智能指针是对C++中的对象回收机制的封装,我们知道C++中的构造函数和析构函数是在对象new出来和delete的时候调用的,但是一个在销毁一个对象的时候,我们需要手动的调用delete关键字来销毁,但是在Java中我们无需在乎这些对象的销毁工作,都是由垃圾回收器来做了,所以在Android系统层,为了达到Java的这个自动管理对象的效果,就出现了智能指针的概念了,他的出现类似于Java中的回收器,或者是OC中的自动释放池的功能等,他内部实现也很简单,就是用两个变量来控制,一个是强引用计数变量,一个是弱引用计数变量,这两个变量都是int类型的,表示一个对象被引用多少次,两个变量会依据具体的生命管理周期模式来决定是否释放对象。


三、源码解读

Android中的智能指针的主要代码是:RefBase.h和RefBase.cpp这两个文件,他们分别位于:

RefBase.h:Android源码目录\frameworks\native\include\utils\RefBase.h

RefBase.cpp:Android源码目录\frameworks\native\libs\utils\RefBase.cpp

我们在RefBase.h中可以看到智能指针的定义。智能指针分为两种,一个是sp,一个是wp,顾名思义,sp=strong pointer

wp=weak pointer,他们两各有用途,后面会详细介绍,首先来看一下他们的定义:


我们看到这个使用到了C++中的模板技术来定义的,关于模板大家如果不熟悉的话,可以认为是Java中的泛型机制,简化代码,增加通用性和安全性的。

从定义来看,我们知道sp和wp其实是一个类,并非真正意义上的指针,这就解决了我们的第一个困惑,智能指针不是真正意义上的指针。

我们看到sp和wp类中都有一个RefBase类型的变量,那么我们在看看RefBase是个什么东东:


这里我们看到,本身RefBase有两个方法:incStrong和decStrong,这个一看应该就知道这两个方法就是用来操作强指针的,在RefBase内部定义了weakref_type类,这个类有两个方法:incWeak和decWeak,这个一看应该就知道这两个方法就是用来操作弱指针的。我们在去RefBase.cpp内部看看:


RefBase内部的一个weakref_impl内部类,继承weakref_type类,他内部有四个变量:

1、mStrong:是记录强指针的引用计数

2、mWeak:是记录弱指针的引用计数

3、mBase:是RefBase类型的变量

4、mFlags:智能指针采用什么样的生命管理周期,默认是0,就是采用强生命周期来管理对象。

接着看到weakref_impl的初始化,看到:

mStrong变量初始化的值是:0x10000000

为什么这里不用0作为初始化的值,因为后面需要判断一个状态就是强引用计数有没有被使用过,如果用0的话,那么就不能区分0值代表是没使用过,还是使用过了清零了,所以这里不用0来做初始化值

mWeak变量的初始化的值是:0

mFlags变量的初始化的值是:0,这里的0表示就是用强生命管理方式去管理对象的生命周期,当然还有其他的方式,看一下定义:


还有一个是弱管理方式,为什么有这两种方式,后面会说到。


分析到这里,我们需要整理一下关系:

1、sp和wp是一个类,他们用来管理一个对象的生命周期,但是他们都是模本类,所有管理的对象都必须继承RefBase类,这个RefBase类叫做真实对象,就是管理对象本身。

2、RefBase类是最核心的类,他内部的一个weakref_impl内部类,继承weakref_type类,这个类我们一般叫做影子对象,和RefBase是相对应的。weakref_impl中有几个变量,mStrong用来记录对象的强引用计数,mWeak用来记录对象的弱引用计数,mBase是RefBase类型的,也就是需要管理对象本身。mFlag是采用哪种方式去管理对象的生命周期。

3、从2中我们可以看出来,RefBase和weakref_impl这两个类是相互依赖的,而且他们也是相互对应的,一个是真实的对象,一个是影子对象。


说完了关系之后,我们在看一下sp到底如何工作的:

假设现在有一个类MyClass,如果要使用智能指针来引用这个类的对象,那么这个类需满足下列两个前提条件:
1)这个类是基类RefBase的子类或间接子类;
2)这个类必须定义虚构造函数,即它的构造函数需要这样定义:
virtual ~MyClass();
满足了上述条件的类就可以定义智能指针了,定义方法和普通指针类似。比如普通指针是这样定义:
MyClass* p_obj;
智能指针是这样定义:
sp<MyClass> p_obj;
注意不要定义成 sp<MyClass>* p_obj。初学者容易犯这种错误,这样实际上相当于定义了一个指针的指针。尽管在语法上没有问题,但是最好永远不要使用这样的定义。
定义了一个智能指针的变量,就可以象普通指针那样使用它,包括赋值、访问对象成员、作为函数的返回值、作为函数的参数等。比如:

p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass>  
sp<MyClass> p_obj2 = p_obj;  
p_obj->func();  
p_obj = create_obj();  
some_func(p_obj);  
注意不要试图delete一个智能指针,即 delete p_obj。不要担心对象的销毁问题,智能指针的最大作用就是自动销毁不再使用的对象。不需要再使用一个对象后,直接将指针赋值为NULL即可:
p_obj = NULL;

上面说的都是强指针,弱指针的定义方法和强指针类似,但是不能通过弱指针来访问对象的成员。下面是弱指针的示例:

wp<MyClass> wp_obj = new MyClass();  
p_obj = wp_obj.promote(); // 升级为强指针。不过这里要用.而不是->,真是有负其指针之名啊  
wp_obj = NULL;  
智能指针用起来是很方便,在一般情况下最好使用智能指针来代替普通指针。但是需要知道一个智能指针其实是一个对象,而不是一个真正的指针,因此其运行效率是远远比不上普通指针的。所以在对运行效率敏感的地方,最好还是不要使用智能指针为好。


下面我们来具体分析RefBase内部的实现,主要看incStrong,decStrong,incWeak,decWeak这几个方法:

1、incWeak方法:


这里的incWeak方法是增加弱应用计数,采用的是android_atomic_inc方法,进行的原子操作,需要注意的是,这个方法的返回值代表是增加前的值。


2、incStrong方法:


这个方法就是增加强引用计数,我们看到首先会调用weakref_impl类型变量中的incWeak方法,增加弱引用计数,然后调用:

const int32_t c = android_atomic_dec(&refs->mStrong);

来增加强引用计数,这里是一个原子操作,返回值注意是操作前的值。接着往下看,有一个判断:

如果强引用计数在操作前是初始化值(INITIAL_STRONG_VALUE),那么这里我们会将强应用计数在减去一个INITIAL_STRONG_VALUE值,那么这时候mStrong的值就是1了,符合预期,然后在调用一下onFirstRef方法,用于初始化的操作。


3、decWeak方法:


这里首先对弱引用计数做减一操作,如果弱引用计数为0了,那么需要做如下判断:

1、如果采用的是强生命周期管理方式:

1)如果没有用过强引用计数这个变量,那么直接删除真实对象

2)如果用过了,那么直接删除影子对象

2、如果采用的是弱什么周期管理方式,直接删除真实对象

这里需要注意的是,因为OBJECT_LIFETIME_MASK和OBJECT_LIFETIME_WEAK的值是一样的都是1,这里的两个判断生命管理方式的代码也就一样的,只是感觉第一个判断有点傻逼了,应该是写源码的时候出差了。


4、decStrong方法:


这个方法中,首先对强引用计数做减一操作,如果强引用计数为0了,那么在判断对象的管理方式是不是强管理方式,如果是直接删除真实对象,最后在调用一次decWeak方法,减少一次弱引用计数,因为在之前的incStrong方法中也是调用了一次incWeak方法,这里也是对应起来了。


5、RefBase的析构方法:


我们在分析上面的decStrong方法中,没有发现删除影子对象的代码,那么影子对象什么时候删除呢?其实是在真实对象的析构方法中做了操作。


我们分析完了上面的几个方法,下面就来整理一下:

1、incStrong和incWeak方法比较简单,就是增加一下强引用和弱引用计数,需要注意的是,incStrong方法中会调用incWeak方法

2、decWeak方法有点复杂,要做的事有两件:是否要释放真实对象,是否要释放影子对象:

1)、如果生命周期是弱引用来控制,那么在这里就需要做判断,弱应用的计数是否为0,是否要释放真实对象和影子对象
2)、如果生命周期是强引用来控制的,那么这里也要判断一下,如果强引用计数为0的话,需要释放真实对象,弱引用计数是        否为0,是否要释放影子对象

从这里我们可以看到:
弱引用计数是关系影子对象的,如果弱引用计数为0,那么影子对象一定要释放,但是真实对象不一定要释放
强引用计数是关系真实对象的,如果强引用计数为0,那么真实对象一定要释放的,但是影子对象不一定释放

3、decStrong方法主要做:就是看看是否要释放真实对象,因为强引用和真实对象关联的

4、RefBase的析构方法:真实对象被销毁的时候,需要做一个工作就是释放影子对象

释放影子对象有两个场景:
1)、如果强引用计数根本没有被使用过,那么直接释放
2)、如果强引用计数使用过,但是采用的是非强生命周期管理方式,也是释放


四、案例测试

上面其实就是介绍完了Android中的智能指针的概念和用法了,但是可能还是不太理解,所以为了更好的理解,我们需要用我们自己最熟悉的代码去实践一下,从写一下,从写的话不难,定义一个RefBase类和weakref_impl类即可,这里我用Java来实现了,具体代码这里就不粘贴了,后面会给出项目的下载地址,感兴趣的同学可以去看一下:

RefBase.java的定义如下:


这里有一点需要注意的是,Java中的对象是自动管理的,没有类似于C++中的析构方法,所以这里就定义一个dealloc来模拟析构方法:


这里的dealloc方法是其实是抄袭OC中的,哈哈~~


weakref_impl.java定义如下:



在定义一个测试类TestA.java:


这里需要继承RefBase类


下面就用测试代码进行测试:


用两个对象来做测试:

第一个对象用来测试:强管理生命周期方式


这里我们可以看到运行结果,符合预期:

如果是采用强生命周期管理对象的话,只有当强引用计数为0的时候,才会删除真实对象,当弱引用计数为0的时候,去删除影子对象。


第二对象用来测试:弱生命周期方式


这里我们可以看到运行结果,符合预期:

如果采用弱生命周期管理对象的话,只有当强引用计数和弱引用计数都为0的时候,才会删除真实对象。

比如,这里我们注释一行代码:


我们在运行结果:


发现,真实对象没有删除,影子对象也没有删除。符合预期


说道这里了,还想在多说一句,就是在Android4.4之前,除了这两种管理方式,还有一种就是:OBJECT_LIFETIME_FOREVER,看字面意思是,永久的,也就是说如果,强引用计数和弱引用计数都为0的时候,这个对象也删除不了,只有手动的调用delete才可以销毁对象,不过这个在Android4.4之后就废弃了,既然说到这里了,就在看一下吧:

在Android4.4之前的RefBase源码中的decWeak方法定义是这样的:


这里看到了,用的是OBJECT_LIFETIME_FOREVER,因为这个值是3=0x11,OBJECT_LIFETIME_WEAK这个值是1=0x01,那么

OBJECT_LIFETIME_FOREVER其实是包含了OBJECT_LIFETIME_WEAK这个情况的,下面我们把Java代码改一下:


运行结果:


无论我们怎么调用dec的各种方法,都是没有删除真实对象和影子对象的,只能手动的调用真实对象的dealloc方法了。不过这个管理方式已经被废弃了,所以我们可以不用在意了。


五、知识梳理

到这里我们就介绍完了Android中的智能指针的相关知识了,下面来整理一下:

1、Android中的智能指针不是真正意义上的指针,他是sp和wp的类对象,用来管理对象的生命周期的中间类。

2、Android中为什么要采用智能指针?因为在Android系统层都是用C/C++实现的,不能自动管理对象的生命周期,所以就开发了一套可以自动管理对象的生命周期机制:智能指针

3、关于智能指针的三种生命周期管理方式:

1). 如果对象的标志位被设置为0,那么只要发现对象的强引用计数值为0,那就会自动delete掉这个对象;
2). 如果对象的标志位被设置为OBJECT_LIFETIME_WEAK,那么只有当对象的强引用计数和弱引用计数都为0的时候,才会        自动delete掉这个对象;
3). 如果对象的标志位被设置为OBJECT_LIFETIME_FOREVER,那么对象就永远不会自动被delete掉,谁new出来的对象谁                来delete掉。


六、补充知识点

前面说到的知识点漏掉两个,但是个人感觉和本篇文章没什么关系,这里就简单说一下:

1、Android中的智能指针其实分为轻量级和重量级,重量级就是我们上面提到的,也是最复杂的,轻量级指针很简单:


这里直接就用一个mCount变量来控制对象的引用次数。

2、上面说到了一个sp和wp两个类,我们知道sp是真实对象的一个指针,可以直接使用真实对象中的方法,wp是影子对象,他只是真实对象的一个引用,不能直接使用真实对象中的方法,我们从他们两的用法既可以看出来,sp用的都是点语法,wp用的都是->语法,这个在C/C++中,点语法就是指针,->语法就是引用。

所以需要将wp升级到sp才能使用真实对象,那么这里需要注意的是,如果真实对象已经delete了,那么wp升级sp之后的对象是为NULL的。

最后来看一下RefBase,weakref_impl,sp,wp他们之间的关系:




七、我们为什么要介绍智能指针

开始的时候已经说了,Android系统层为了解决对象的自动管理就引入了智能指针机制,所以智能指针是我们后续文章的基础,后面再分析系统模块的时候,会发现很多类都用到了RefBase,比如Binder机制:


这个也是我们后面需要分析的一个模块,看到了他就用到了RefBase。


项目下载地址:http://download.csdn.net/detail/jiangwei0910410003/9509009


八、总结

这一篇就介绍完了Android中的系统篇中的基础,智能指针的知识点,当然这里可能说的不是那么全面,但是我们可以看懂了,自己写一个例子来深入了解一下智能指针。


更多详细内容可以   点击这里


PS: 关注微信,最新Android技术实时推送

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
C++中不要使用包含中文字符串,否则打包的时候编译错误();虚幻中提供了使用中文的工具:本地化。 1. 启用本地化功能 这样,在菜单“窗口”项就会出现“本地化面板” 2. 使用本地化    a. 添加目标              b.修改Config中 DefaultEngine.ini。    添加下面内容:     [Internationalization]     +LocalizationPaths=/Game/Localization/Game   c.添加收集目录       d.添加新语
/prep最近半年来身边开发的朋友越来越多的提到OkHttp,上谷歌百度一下,确实OkHttp成了时下最火的HTTP框架,于是我也开始放下Volley,转而关注OkHttp,五一期间仔细看了官方WiKi介绍(我喜欢学习官方的文档),现在把自己整理的官方教程分享给大家,希望给初学者带来帮助。 br /       OkHttp官网地址:a target=_blank href=#>        OkHttp perseveres when the network is troublesome: it wi

Activity启动模式学习 - 2016-05-04 17:05:31

最近学习了Activity的启动模式,这里记录下,以便以后回顾 Activity有四种启动模式,分别为: * standard * singleTop * singleTask * singleInstance standard 默认情况下,系统会以standard模式来启动activity,这种模式下系统会创建一个新的activity实例。 singleTop 指定Activity的启动模式为singleTop时,当被启动的Activity B已经存在一个实例,并且位于任务栈的栈顶,这时候系统就不会生成
Current git graph Now we want to create a new branch called  dev  from our master branch. Here is our current state of our git graph before the branching:   Creating a dev branch To create a new branch ( dev ), we need click "Branch" on the top menu.   Hi
1 背景 这篇文章主要说明的是用它来看源码的相关配置(该实例依据官方源码进行配置说明)。在android源码中发现有一个development/tools/idegen目录,查了一下发现是生成IDE的project文件(主要是intellij的project文件,其实也就是Android Studio的),所以说福利工具就这么来了,欲知详情,请看下文。 2 配置使用过程 2-1 基础准备工作 首先你得安装配置了Android Studio,接着你得下载好了源码Code,至于如何下载这里不再说明,比较简单,

AsyncTask获取数据库数据 - 2016-05-04 17:05:10

2016_04_25 —————R 关于 http://blog.csdn.net/u010045971/article/details/50853651 未尽之事 1. 为应用程序添加provider的访问权限。 在manifest标签内,定义上面的提到的 permission permission android:name = "com.example.android.sunshine.app.LICENSE_TO_KILL" android:protectionLevel = "dangerous"
activity 生命周期 完整生命周期   oncreate-- 》 onstart-- 》 onresume-- 》 onpause-- 》 onstop-- 》 ondestory   可视生命周期   onstart-- 》 onresume-- 》 onpause-- 》 onstop   前台生命周期   onresume-- 》 onpause   界面用户仍然可见,但是失去焦点     使用场景: 1. 应用程序退出自动保存数据    ondestory   oncreate 2. 应用程
上方谷司马受困 五丈原诸葛禳星 却说司马懿被张翼、廖化一阵杀败,匹马单枪,望密林间而走。张翼收住后军,廖化当先追赶。看看赶上,懿着慌,绕树而转。化一刀砍去,正砍在树上;及拔出刀时,懿已走出林外。廖化随后赶出,却不知去向,但见树林之东,落下金盔一个。廖化取盔捎在马上,一直望东追赶。原来司马懿把金盔弃于林东,却反向西走去了。廖化追了一程,不见踪迹,奔出谷口,遇见姜维,同回寨见孔明。张嶷早驱木牛流马到寨,交割已毕,获粮万余石。廖化献上金盔,录为头功。魏延心中不悦,口出怨言。孔明只做不知。 且说司马懿逃回寨中,心甚

ProGuard详解 - 2016-05-04 15:05:59

综述 对于ProGuard工具想必我们都不陌生,它能够通过移除无用代码,使用简短无意义的名称来重命名类,字段和方法。从而能够达到压缩、优化和混淆代码的目的。最终我们会获取一个较小的apk文件,并且我们这个通过ProGuard处理的apk文件更难于进行逆向工程。 ProGuard工作原理简介 ProGuard能够对Java类中的代码进行压缩(Shrink),优化(Optimize),混淆(Obfuscate),预检(Preveirfy)。 1. 压缩(Shrink):在压缩处理这一步中,用于检测和删除没有使

Unity3D UNET 模仿局域网游戏 - 2016-05-04 15:05:55

这两天在学习的过程中,发现一个讲Unity的unet组件的好教程,在这里分享给大家。 新建一个 UnetProject。 新建一个GameObject重命名为Network Manager,给它添加Network Manager组件,这是Unet提供的一个核心管理组件,可以在脚本里访问Network Manager进行网络开发。 还需要添加一个 Network Manager HUD 组件,这个用来显示UI,UI上的按钮会跟Network Manager交互。这时候运行屏幕上会出现如下: LAN Host