Activity的内部机制
来源:互联网 发布:mac怎么删除第三方软件 编辑:程序博客网 时间:2024/05/16 00:42
文章来自华初网【陈小贤转】
分析完AmS内部机制后,读者们已经明白,系统一次只允许一个Activity处于运行状态,要让另外一个Activity处于运行状态必须先暂停当前正在运行的Activity。而在应用程序设计时,Framework提供了一个ActivityGroup类,它允许把一个Activity嵌入到另一个Activity中运行,常见的基于该类的实现包括TabActivity,这到底是怎么回事呢?难道ActivityGroup类具备同时使多个Activity处于运行状态的能力吗?
本节就以TabActivity类的实现为例,说明ActivityGroup的内部机制。
10.4.1 TabActivity使用时的类关系结构
在使用TabActivity设计应用程序时,其内部相关的类关系如图10-24所示。
图10-24 TabActivity相关类的关系
首先TabActivity的祖先类也是Activity类,因此,从AmS的角度来看,TabActivity与普通的Activity没有什么区别,其生命期包括标准的start、stop、resume、destroy等,而且系统中只允许同时运行一个TabActivity。
所不同的是,TabActivity基于ActivityGroup类,该类的父类是Activity类,但ActivityGroup内部有一个重要成员变量,其类型为LocalActivityManager,该类的最大特点在于它可以访问应用进程的主类,即ActivityThread类。前面分析过,AmS要启动某个Activty或者暂停某个Activity都是通过ActivityThread类执行的,而LocalActivityManager能够访问ActivityThread类就意味着可以通过它来装载不同的Activity,并且控制Activity的不同的状态。
为什么LocalActivityManager可以访问主类ActivityThread,而普通的应用程序无法访问主类呢?因为在编译输出Android SDK时,ActivityThread类是被隐藏的,如以下代码所示:
所以,应用程序无法通过SDK来引用ActivityThread类。那又有读者问了,如果不使用SDK编译,而直接使用源码编译是否可用呢?真的很抱歉,也是不可以,因为客户进程的主类变量的访问权限被设置为包内访问,如以下代码所示,未加权限限定符即指包内访问。
而LocalActivityManager正是和ActvityThread在同一个包内,这就是为什么它能够访问主类对象的原因。
TabActivity类的默认布局文件是com.android.internal.R.layout.tab_content,该布局的内容是一个TabHost视图,其id值为系统id值android:id/tabhost。TabHost中包含了两个子视图,一个是TabWidget,其id值必须是android:id/tabwidget,这就是大家经常看到的Tab页的效果;另一个是FrameLayout视图,其id值必须是android:id/tabcontent,TabActivity所包含内置Activity对应的窗口就是被添加到该FrameLayout中显示的。
tab_content仅仅是TabActivity的默认布局,应用程序可以使用自定义的布局代替这个布局,但自定义的布局文件中必须包含tab_content中所声明的最少三个视图,并且其id值必须是系统的id值,分别为TabHost、TabWidget、TabContent。
在TabHost的视图类中,包含了三个重要的函数或变量,分别如下。
— setup(LocalActivityManager):该方法用于给TabHost内部设置一个LocalActivityManager对象。读者可能会觉得奇怪,TabHost中为什么还需要这个Manager对象呢?这个对象的确不是必需的,假如你所实现的Tab页视图不需要嵌入一个Activity视图,那么就不需要设置这个对象,否则必须告诉TabHost这个Manager对象,要不然TabHost是无法获得指定Activity的内部视图的。
— addTab(TabSpec):该方法用于向TabHost中的TabWidget视图添加一个Tab页。表面上看,既然是添加一个Tab页,那么TabSpec类应该是一个视图类才对,而事实上TabSpec是一个功能类,其本身并不是一个视图,但是它却可以提供产生视图的方法。
— List<TabSpec> mTabSpecs:这是TabHost中所包含的全部Tab页列表,每一项都是一个TabSpec对象。
下面,再来介绍TabSpec是如何产生Tab页视图的。
TabSpec类中有两个重要变量,其类型分别是IndicatorStrategy和ContentStrategy。这两个子类的名称有点意思,Indicator的意思是“指示”,它指示用户该Tab页的意义,Content即为单击某个Tab页后对应的“内容”,而Strategy是策略的意思,因此这两个子类的作用分别是“产生Tab页的策略”和“产生内容的策略”。同时,这两个子类仅仅是一个interface,为什么只是一个接口呢?因为既然是策略就意味着可以有不同的策略,在TabHost中也的确实现了不同的策略,其中Tab页策略包括以下几项。
— LableAndIconIndicatorStrategy:可以产生一个带图标和标签文字的标签页。
— LableIndicatorStrategy:仅仅有标签文字的标签页。
— ViewIndicatorStrategy:可以使用自定义View作为标签页视图。
内容策略包括以下。
— FactoryContentStrategy:使用回调方式允许应用程序动态创建内容视图。
— IntentContentStrategy:嵌入Activity视图作为内容视图,这正是TabActivity所使用的。
— ViewIdContentStrategy:使用一个静态的布局作为内容视图。
看了这么多的策略后,大家可能会觉得奇怪:“新建一个TabSpec后,如何指定其标签和内容使用何种策略呢?”事实上,当新建一个TabSpec对象后,TabSpec提供了三个设置标签策略的函数setIndicator(xxx),其中不同参数对应不同的标签策略;同时也提供了三个设置内容策略的函数setContent(xxx),不同的参数对应不同的内容策略。其中setContent(Intent intent)函数正是使用嵌入Activity方式,参数Intent用来匹配相应的Activity。
10.4.2 LocalActivityManager的内部机制
LocalActivityManager内部机制的核心在于,它使用了主线程对象mActivityThread来装载指定的Activity。注意,这里是装载,而不是启动,这点很重要。
所谓的启动,一般是指会创建一个进程(如果所在应用进程还不存在)运行该Activity,而装载仅仅是指把该Activity作为一个普通类进行加载,并创建一个该类的对象而已,而该类的任何函数都没有被运行。
LocalActivityManager提供了一个重要方法startActivity(),该方法正是利用主线程mActivityThread去装载指定的Activity,其执行过程如图10-25所示。
图10-25 LocalActivityManager类中startActivity()的执行流程
Manager对象必须已经被初始化,初始化的工作是在dispatchCreate()方法中首先被完成的,而这又是在ActivityGroup类中的onCreate()中被调用的。也就是说,LocalActivityManager的startActivity()方法必须在所在的Activity的onCreate()方法执行完毕后被调用。
判断目标Activity是否包含在该Manager中。Manager中使用两个列表变量保存已经装载过的Activity对象,分别是mActivities和mActivityArray。前者是一个HashMap类型,每一个LocalActivityRecord按照一个字符串对应;后者是一个ArrayList列表。
判断装载的Activity对象是否有正处于resume状态的,如果有,则要先暂停,事实上可以完全不用暂停,暂停仅仅是Manager希望完全按照Activity对象本身的执行顺序调用它,从而使得看上去更像是一个标准的子Activity启动方式。而暂停则是通过调用moveToState()完成的。
如果目标Activity已经被装载到了当前Manager中,下面就需要判断是直接使用该Activity的当前窗口呢,还是需要先销毁该Activity,并重新调用其onCreate()?注意,这里所说的销毁仅仅是指把Activity变成“销毁”的状态而已,并不是说销毁该Activity对象。而判断的规则有点类似于AmS中根据Activity的flag执行不同的操作,其中包括是否先调用目标Activity的onNewIntent(),还包括是否是CLEAR_TOP模式。一般作为TabActivity的嵌入式Activity都不会是CLEAR_TOP模式,否则,如果多个Tab页使用同一个Activity对象将导致所显示的内容完全相同。
调用moveToState()改变指定Activity到resume状态。
返回Activity所对应的Window窗口。
从以上步骤可以看出,装载Activity对象的过程对AmS来讲是完全不可见的,因为这是装载而不是启动,因此看似TabActvity同时运行了多个Activity,而实际上仅仅是运行了ActivityGroup一个Activity。那些嵌入的Activity仅仅是贡献了自己所包含的Window窗口而已,TabActivity正是把这些Window窗口的DecorView作为tabcontent的子视图而已。
下面对moveToState(LocalActivityRecord r, int desireState)函数的过程进行说明,参数r代表目标Activity对象,desireState代表期望把目标Activity改变成哪种状态。
moveToState()函数内部首先判断r.curState是否是RESTORED或者DESTROY状态,如果是则直接返回。因为RESTORED代表刚刚创建了目标Activity对象,还没有执行onCreate()方法,所以不能改变状态;DESTROY代表已经销毁,也不能改变状态。
接着,判断r.curState是否是INITIALIZE状态,这种情况只有在第一次调用startActivity()装载目标Activity对象时才会执行到,其内部主要包括调用startActivityNow()和performResumeActivity()将目标Activity改变到STARTED或者RESUMED状态。
由于Activity当前状态不同,要想达到不同的期望状态自然需要经过不同的步骤。moveToState()函数内部正是使用switch语句先判断当前处于什么状态,然后再在case里面使用if…else语句判断期望的状态,最后再调用不同的函数。其状态和调用关系如表10-8所示,该表中调用的函数名称使用了简写,比如performRestartActivity简写为Restart。
表10-8 Activity在不同状态中转换时需执行的操作
目标状态
当前状态
CREATED
STARTED
RESUMED
CREATED
Restart ();
Restart();
Resume();
STARTED
Stop();
Resume();
RESUMED
Pause();
Stop();
Pause();
从以上的步骤可以看出,startActivity()的内部执行逻辑有点像AmS中根据当前Activity状态调用不同方法。这两者就像《西游记》中的小雷音寺和大雷音寺,两者的本质区别在于LocalActivityManager仅仅是为了获取Activity对应的Window对象,中间的状态切换仅仅是为了保证Activity本身的执行过程,从而保证Window对象的视图内容有一个正确的呈现。
10.4.3 ActivityGroup内部的Activity生命期控制
前面分析了LocalActivityManager内部的执行原理,接下来分析一个有意思的问题:“在TabActivity的多个Tab页切换时,内嵌的Activity对象会在onPause()和onResume()之间切换吗?”
从上面的分析可知,Manager装载Activity的目的仅仅是为了获取其所包含的Window对象,而一旦获取后,则似乎不需要再纠缠于Activity本身的生命期状态变换操作。其实笔者也是这么认为的,可以尝试屏蔽以下代码,该段代码的作用是在装载下一个Activity之前先暂停当前的Activity对象。
屏蔽后,运行结果丝毫不受影响,原因很简单,这里做暂停的目的仅仅为了保持Activity原有的生命期过程,从而可以保持原有Activity释放相关资源的行为。比如当Activity A以启动的方式运行时,如果另一个Activity B要启动,则会先暂停A。在一般的程序设计中,暂停会回调onPause()操作,如果该Activity使用了大量的内存或者其他资源,在onPause()函数中程序员可能会尝试释放这些资源以提高系统效率,这就是为什么在LocalActivityManager中也保持了这种流程的原因。当然,如果你不释放,也不会发生什么逻辑错误。
而在以上代码的moveToState()操作中调用了mActivityThread的onPause()或者onStop()操作,这就是为什么在Tab页切换时,对应的Activity也会执行onPause()或者onStop()的原因。这完全与AmS无关,因此不要因为这个而产生TabActivity同时运行了两个Activity(TabActivity本身和嵌入的Activity)的错觉。
另外还有一个有意思的问题,请思考下面的操作过程。
打开一个TabActivity,比如“联系人”程序,在上面的四个Tab页上都点一次,然后再按“Home”键回到桌面,然后再从联系人图标中进入该程序。请思考此时TabActivity内嵌的Activity会发生生命期状态改变吗?
首先TabActivity当然会从stop状态转变为start状态,并先后调用onStart()和onPause()。因为它是一个标准的Activity,TabActivity的父类是ActivityGroup,而在该类中,相应的onXXX()方法内部都增加了mLocalActivityManager.dispatchXXX()代码,比如:
而在LocalActivityManager中,dispatchXXX()则会把相应的action再dispatch到所包含的所有嵌入式Activity对象中。所以,以上问题的答案是内嵌的Activity生命期会从stop状态转换到resume状态。仔细查看ActivityGroup的onXXX()函数发现,唯独没有onStart()方法,其原因是在LocalActivityManager的moveToState()方法中可以直接把子Activity的状态从stop改变到resume,所以,此处可以省略对onStart()的重载。
- Activity的内部机制
- Activity本质和内部实现机制
- strcpy的内部机制
- LocalActivityManager的内部机制
- 线程的内部机制
- LocalActivityManager的内部机制
- ThreadLocal的内部机制
- PendingIntent的内部机制
- LocalActivityManager的内部机制
- PendingIntent的内部机制
- PendingIntent的内部机制
- Window的内部机制
- Hashmap的内部机制
- PendingIntent的内部机制
- PendingIntent的内部机制
- PendingIntent的内部机制
- RemoteViews的内部机制
- YATE内部的消息机制
- 【动态规划】zb的生日
- 交通灯调度系统
- IOS中Notificaiton通知的使用方法<代码演示>
- MD5加密算法(2)
- CodeChef Mike and Stamps
- Activity的内部机制
- asp.net经典问题之“未将对象引用设置到对象的实例”
- Android--Apache HttpClient
- 蓝桥杯-危险系数
- poj1149最大流经典构图神题
- HDU 1701
- Oracle 表的管理
- 变量在内存中的存放
- Strongly Typed Enums