Activity管理(三):activity内核管理方案详细讲解

一、Activity的生命周期

Activity的管理同样是基于C/S架构的,所有的activity管理都在server端进行。在Server端对每个activity进行调度的同时,Client端负责响应各个生命周期的函数。

在Client端,对activity各个生命周期的响应都是在ActivityThread里进行操作。在本地,activity的生命周期如下图

img

ActivityThread提供给Server端的调度接口有以下几个:

scheduleLaunchActivity()

scheduleResumeActivity()

schedulePauseActivity()

scheduleStopActivity()

scheduleDestoryActivity()

scheduleRelaunchActivity()

scheduleWindowVisibility()

除了以上几个接口之外,还有其他的诸如scheduleConfigurationChanged()、scheduleNewIntent()这些接口,因为这些接口没有影响到activity的生命周期的流程,所以这里就不一一列举。下面我们来挑两个主要的接口来分析在本地端的调用流程。

1.ScheduleLaunchActivity

此接口的作用在于生成一个新的本地Activity对象,并且使之进入resume状态(或pause状态)。通常AMS会在两种情况下调用这个接口:点击应用图标打开新应用;从Activity B返回Activity A时,Activity A由于内存不足被kill。至于AMS如何确定何时调用此接口,属于AMS内部对activity的调度流程,后面会分析到。

在这里先说明下ActivityThread各函数的命名规则和函数调用方式。

上面列举的几个提供个AMS的接口全都以schedule为前缀,并且所有接口都是往主线程的handler里发送了命令消息之后立刻返回。这样所有的操作都会被转移到主线程上来。对应每一个schedule函数,都有一个响应操作的handle函数。在handle函数里,会根据需要把操作进一步分解成几个动作,真正面向activity的操作统一命名为perform。在perform***函数的内部,才会真正调用activity的生命周期函数(其实是通过Insturmentation来间接调用)。所以我们可以把所有的接口和方法进行分层:schedule->handle->perform->activity/Insturmation。

在scheduleLaunchActivity中,会往主线程handler发送一个“LAUNCH_ACTIVITY”消息,然后调用handleLaunchActivity。在handleLaunchActivity内部分解成两个动作:

a) performLaunchActivity

在这里完成了activity对象的创建和activity生存环境的创建。比如application对象的生成。接着调用activity.attach()完成Window和WindowManager的创建,并且记录了activity的环境变量如mToken、mActivityInfo等。到此为止,一个具有完整功能的Activity对象已经完成了初始化,接着会调用mInstrumentation.callActivityOnCreate()和activity.performStart()分别响应属于activity生命周期的onCreate()方法和onStart()方法。

b) handleResumeActivity

这个方法属于“handle层”的另一个方法,主要完成onStart()和onResume()的响应,在第二个例子中会说明。

2.ScheduleResumeActivity

顾名思义,此方法的作用是把一个activity置于resume状态。相应的,所有操作被转移到handleResumeActivity()中。对于handleResumeActivity,有两种情况会调用此方法:scheduleResumeActivity发送“RESUME_ACTIVITY”消息;handleLaunchActivity中的第二步。

在handleResumeActivity中也进一步分解成了两个动:

a) performResumeActivity

调用activity.performResume()方法,在activity.performResume()中会默认先调用performRestart(),performRestart()的作用是先检查activity是否处于stopped状态,如果是则调用performRestart()再调用performStart()。最后再响应activity的生命周期方法onResume()。

所以,新打开一个activity的响应流程是:

onCreate()->onStart()->onResume()

而返回一个后台activity的响应流程是:

onRestart()->onStart()->onResume()

b) 完成了第一步对activity生命周期的响应之后,在第二步这里主要是处理窗口的添加动作:把DecorView添加到WindowManager中。完成第二步操作之后,activity的内容便显示到了屏幕上。这个窗口的添加动作属于窗口管理中的一个步骤,具体可参考《Android窗口管理剖析》一文。

以下是部分主要接口到生命周期的响应的内部调用流程图

img

二、ActivityManagerService及其内部调度流程

\1. 数据结构分析

和窗口管理系统一样,所有的客户端activity在ActivityManagerService(简称AMS)内部都会有一个对应的ActivityRecord,对activity的管理也就是对ActivityRecord的管理。

AMS的相关代码在framework/base/services/java/com/android/server/am,主要的数据结构有:

ActivityManagerService

ActivityStack

ActivityRecord

TaskRecord

ProcessRecord

  1. ActivityManagerService是android框架服务,主要负责处理对android四大组件的管理和响应Client端的请求。此外还包括进程的产生和对WindowManagerService的操作。

  2. ActivityStack是专门实现对ActivityRecord的堆栈式管理而分离出来的一个模块。在Android2.2以前,对ActivityRecord的管理和调度都在ActivityManagerService实现。从android2.3以后,为了实现更好的解耦把对ActivityRecord的管理单独分离出来了,所有对ActivityRecord的调度操作都在ActivityStack里进行。其实Android里所谓的“Activity堆栈”,并不是真正的一个堆栈结构,而是一个ArrayList列表,在这个列表里记录了所有的ActivityRecord,ActivityStack所做的事情就是保证列表的第一个ActivityRecord(也就是“栈顶”)处于运行状态,并且排在列表后面的ActivityRecord的运行状态受到“栈顶”ActivityRecord的影响。

  3. ActivityRecord代表了一个Client端的Activity,记录了Activity的各种属性和管理状态。其中有介个关键的成员变量:

IApplicationToken.StubappToken

appToken作为此ActivityRecord的唯一标识,贯穿了AMS、Activity和WMS。

TaskRecordtask

task标识了此ActivityRecord所属的Task。在ActivityStack对ActivityRecord的位置调整中,更多的是以TaskRecord为单位进行的。如moveTaskToFront()、moveTaskToBack()等接口,会同时移动属于同一个Task的所有activity。 TaskRecord和ActivityRecord是一对多的关系,多个ActivityRecord可能指向同一个TaskRecord

ProcessRecordapp

app标识了Client端的Activity所在的进程。同样的,ProcessRecord和ActivityRecord是一对多的关系,多个ActivityRecord可能指向同一个ProcessRecord。通过访问ProcessRecord中的IApplicationThread可以直接操作Client端的ActivityThread。

  1. TaskRecord记录了task的id、名称等属性。

我们知道,Activity的管理是以Task为单位来进行的,多个行为类似的Activity会被归类到同一个Task中。一般来说,一个apk中的所有Activity都是属于同一个Task,并且Task以apk的包名来命名。但开发者可以在AndroidManifset.xml中通过android:taskAffinity属性给每个Activity配置不同的Task。而什么时候会新开一个Task,新开Task的名字等由ActivityStack里面的逻辑来判断,具体取决于AndroidManifest.xml中的配置和调用startActivity()时传递的flag。

  1. ProcessRecord记录了进程id、进程名字和各种用于调节优先级的状态。还包含了所有运行在该进程的Activity、Service、Receiver、Provider等组件。IApplicationThread thread记录了该进程中的主线程。当一个Acitivity第一次启动时,会首先调用Process.start(“android.app.ActivityThread”)来创建一个新进程并且生成一个ProcessRecord对象。当进程运行起来之后,该进程的主线程会调用Client端的ActivityThread.main()函数,在main()函数中完成Looper的prepare(),并且生成ActivityThread对象,接着在ActivityThread.attach()中调用IActivityManager.attachApplication()回到AMS,在AMS中完成ProcessRecord和IApplicationThread对象的绑定。接下来的流程就会把新Activity和ProcessRecord绑定。

关于ActivityManagerService、ActivityStack、ActivityRecord、TaskRecord、ProcessRecord的关系可用下图表示:

img

\2. ActivityStack内部调度流程

在整个调度流程中,ActivityStack记录了列表中每个ActivityRecord的当前状态,包括这9个状态:INITIALIZING, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED,FINISHING, DESTROYING, DESTROYED。每个ActivityRecord在任何时刻都处于这9个状态中的其中一个。除此以外,ActivityStack还有几个变量用于辅助调度过程的:

ActivityRecord mResumedActivity

ActivityRecord mPausingActivity

boolean finishing

boolean stopped

ArrayList<ActivityRecord>mStoppingActivities

mResumedActivity指向当前系统中的“栈顶”Activity,当“栈顶”Activity将要被暂停时,mResumedActivity为空,mPausingActivity指向该被暂停的Activity,直到Client端Activity响应onPause()之后回调AMS,mPausingActivity置空。finishing和stopped是ActivityRecord的两个boolean变量,finishing为true表示该Activity将要被销毁;stopped为true表示该Activity处于stopped状态(该stopped变量和Activity的STOPPED状态有重复的嫌疑,可认为是同等的)。

ActivityStack内部的调度围绕着这9个状态来进行,下面通过例子来分析其调度流程。

从Launcher(A)打开新应用(B)

  1. 在Clien端请求打开新Activity之后,ActivityStack首先创建一个ActivityRecord并将其置于“栈顶”,此时ActivityRecord处于INITIALIZING状态。

  2. 接着调用resumeTopActivityLocked()。在resumeTopActivityLocked()中检查mResumedActivity是否为空,此时mResumedActivity指向A,所以肯定不为空。

  3. 调用startPausingLocked()暂停A。此时将mResumedActivity置空,mPausingActivity指向A, 并且将A的状态置为PAUSING。接着调用Activity A的Client端的schedulePauseActivity()将其暂停。

  4. Client端的Activity 响应onPause()并且回调AMS的activityPaused()方法,将A的ActivityRecord的状态置为PAUSED,并把A添加到mStoppingActivities中。完成了A的暂停处理之后,会重新回到第2)步。在第二步中,此时mResumedActivity为空,接下来的流程会最终调用到Activity B 的Client端的scheduleLaunchActivity()。此时Activity B的状态变为RESUMED。

  5. ActivityB的Client端完成Activity的new和初始化并且响应onCreate()->onStart()->onResume(),然后回调AMS的activityIdle()方法告诉AMS已经运行完毕处于空闲状态。

  6. activityIdleLocked()主要是完成经过“堆栈”经过调整之后的“善后”工作。如根据finishing状态完成挂起的销毁操作,处理mStoppingActivities中的挂起stop操作。经过第4)步,A已经在mStoppingActivities中,所以接下来会调用A的Client端的scheduleStopActivity(booleanvisible),在Client端,根据接口参数visible判断,如果visible为false则响应onStop(),否则不响应。在ActivityStack中,visible的值是通过从上到下的计算得出的。如果“栈顶”Activity为fullscreen,则处于“栈顶”底下的其他activity的visible全为false。这个计算过程在第4)步中会执行一次或多次。在执行stop操作时,Activity A的状态从PAUSED变成STOPPING,再变成STOPPED。

在第4)步的处理中,会继续调用resumeTopActivityLocked(),在resumeTopActivityLocked()中,会判断ActivityRecord中的ProcessRecord app是否为空,这里有两种情况:

a) app不为空。

这种情况的前提是,Activity B并不是新打开的而是在一直在后台,并且其进程没有由于内存回收而杀掉。在这种情况下,会直接调用scheduleResumeActivity(),Client端会进行响应:onRestart()->onStart()->onResume()

b) app为空

app为空有三种情况,第一种是ActivityB之前一直在后台运行,由于内存不足被杀掉;第二种是Activity B是新打开的,并且不存在对应的进程;第三中情况是,Activity B 是新打开的,但是其进程仍在运行中。在第一、二种情况中,AMS会启动新进程,并完成Activity B和新进程的ProcessRecord的绑定。

在这三种情况中,最后都会调用scheduleLaunchActivity(),Client端会进响应:onCreate->onStart()->onResume()。

所以,从Launcher(A)打开新Activity (B)的生命周期响应流程如下:

A->onPause()

B->onCreate()

B->onStart()

B->onResume()

A->onStop() (如B不为fullscreen则不响应此方法)

整个调度流程可用下图表示:

img

关于ActivityStack内部的其他参与调度的函数,大概整理了一下,得到下面的函数调用流程图

img

转自:http://www.voidcn.com/blog/huanxido/article/p-3139817.html

推荐阅读更多精彩内容