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没有什么区别,其生命期包括标准的startstopresumedestroy等,而且系统中只允许同时运行一个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值为系统idandroid:id/tabhostTabHost中包含了两个子视图,一个是TabWidget,其id值必须是android:id/tabwidget,这就是大家经常看到的Tab页的效果;另一个是FrameLayout视图,其id值必须是android:id/tabcontentTabActivity所包含内置Activity对应的窗口就是被添加到该FrameLayout中显示的。

tab_content仅仅是TabActivity的默认布局,应用程序可以使用自定义的布局代替这个布局,但自定义的布局文件中必须包含tab_content中所声明的最少三个视图,并且其id值必须是系统的id值,分别为TabHostTabWidgetTabContent

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类中有两个重要变量,其类型分别是IndicatorStrategyContentStrategy。这两个子类的名称有点意思,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()中被调用的。也就是说,LocalActivityManagerstartActivity()方法必须在所在的ActivityonCreate()方法执行完毕后被调用。

 判断目标Activity是否包含在该Manager中。Manager中使用两个列表变量保存已经装载过的Activity对象,分别是mActivitiesmActivityArray。前者是一个HashMap类型,每一个LocalActivityRecord按照一个字符串对应;后者是一个ArrayList列表。

 判断装载的Activity对象是否有正处于resume状态的,如果有,则要先暂停,事实上可以完全不用暂停,暂停仅仅是Manager希望完全按照Activity对象本身的执行顺序调用它,从而使得看上去更像是一个标准的子Activity启动方式。而暂停则是通过调用moveToState()完成的。

 如果目标Activity已经被装载到了当前Manager中,下面就需要判断是直接使用该Activity的当前窗口呢,还是需要先销毁该Activity,并重新调用其onCreate()?注意,这里所说的销毁仅仅是指把Activity变成“销毁”的状态而已,并不是说销毁该Activity对象。而判断的规则有点类似于AmS中根据Activityflag执行不同的操作,其中包括是否先调用目标ActivityonNewIntent(),还包括是否是CLEAR_TOP模式。一般作为TabActivity的嵌入式Activity都不会是CLEAR_TOP模式,否则,如果多个Tab页使用同一个Activity对象将导致所显示的内容完全相同。

 调用moveToState()改变指定Activityresume状态。

 返回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里面使用ifelse语句判断期望的状态,最后再调用不同的函数。其状态和调用关系如表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()操作中调用了mActivityThreadonPause()或者onStop()操作,这就是为什么在Tab页切换时,对应的Activity也会执行onPause()或者onStop()的原因。这完全与AmS无关,因此不要因为这个而产生TabActivity同时运行了两个ActivityTabActivity本身和嵌入的Activity)的错觉。

另外还有一个有意思的问题,请思考下面的操作过程。

打开一个TabActivity,比如“联系人”程序,在上面的四个Tab页上都点一次,然后再按“Home”键回到桌面,然后再从联系人图标中进入该程序。请思考此时TabActivity内嵌的Activity会发生生命期状态改变吗?

首先TabActivity当然会从stop状态转变为start状态,并先后调用onStart()onPause()。因为它是一个标准的ActivityTabActivity的父类是ActivityGroup,而在该类中,相应的onXXX()方法内部都增加了mLocalActivityManager.dispatchXXX()代码,比如:

     

而在LocalActivityManager中,dispatchXXX()则会把相应的actiondispatch到所包含的所有嵌入式Activity对象中。所以,以上问题的答案是内嵌的Activity生命期会从stop状态转换到resume状态。仔细查看ActivityGrouponXXX()函数发现,唯独没有onStart()方法,其原因是在LocalActivityManagermoveToState()方法中可以直接把子Activity的状态从stop改变到resume,所以,此处可以省略对onStart()的重载。

0 0
原创粉丝点击