一、前言
今天我们开启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中的系统篇中的基础,智能指针的知识点,当然这里可能说的不是那么全面,但是我们可以看懂了,自己写一个例子来深入了解一下智能指针。
更多内容:点击这里
关注微信公众号,最新Android技术实时推送
转载请注明:尼古拉斯.赵四 » Android系统篇之—-Android中的智能指针