Android系统篇之

Wesley13
• 阅读 464

一、前言

今天我们开启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,他们两各有用途,后面会详细介绍,首先来看一下他们的定义:

Android系统篇之

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

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

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

Android系统篇之

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

Android系统篇之

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

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

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

3、mBase:是RefBase类型的变量

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

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

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

Android系统篇之

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

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

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

Android系统篇之

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

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

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 p_obj;
注意不要定义成 sp* 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方法:

Android系统篇之

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

2、incStrong方法:

Android系统篇之

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

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

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

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

3、decWeak方法:

Android系统篇之

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

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

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

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

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

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

4、decStrong方法:

Android系统篇之

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

5、RefBase的析构方法:

Android系统篇之

我们在分析上面的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的定义如下:

Android系统篇之

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

Android系统篇之

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

weakref_impl.java定义如下:

Android系统篇之

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

Android系统篇之

这里需要继承RefBase类

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

Android系统篇之

用两个对象来做测试:

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

Android系统篇之

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

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

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

Android系统篇之

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

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

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

Android系统篇之

我们在运行结果:

Android系统篇之

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

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

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

Android系统篇之

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

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

Android系统篇之

运行结果:

Android系统篇之

无论我们怎么调用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中的智能指针其实分为轻量级和重量级,重量级就是我们上面提到的,也是最复杂的,轻量级指针很简单:

Android系统篇之

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

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

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

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

Android系统篇之

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

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

Android系统篇之

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

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

八、总结

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

更多内容:点击这里

关注微信公众号,最新Android技术实时推送

Android系统篇之

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Karen110 Karen110
2年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
2年前
PhoneGap设置Icon
参考:http://cordova.apache.org/docs/en/latest/config\_ref/images.html通过config.xml中的<icon标签来设置Icon<iconsrc"res/ios/icon.png"platform"ios"width"57"height"57"densi
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这