Android Framework(四)——ActivityManagerService
注意(WARNING):本文所贴源码全部来自 Android API 29 Platform,即 Android 10.0。
在前面的几篇文章中,AMS或者ATMS绝对是出现频率最高的缩写之一。关于ActivityManagerService(下文简称AMS),我们或多或少都知道它的一点职责,例如四大组件的启动和管理、管理ANR状态、发起应用程序进程启动请求等。
AMS可以说是Android系统服务中最核心的一个服务,仅ActivityManagerService.java类中就有2万5千行代码,Android 10中对此类进行了拆解,其中Activity生命周期管理相关的代码都放到了一个新类ActivityTaskManager(下文简称ATMS)中。即便如此,Android 10中的AMS.java类依旧有1.9万行代码,可见AMS的重要性。
本文主要涉及AMS启动、获取、以及Activity栈管理。
AMS启动过程
在Framework系列的第一篇文章中,我们知道Android系统服务在SystemServer进程创建后,通过其run()方法来启动各类服务:
frameworks/base/services/java/com/android/server/SystemServer.java
1 |
|
并且在文章中以ATMS为例介绍了服务的启动过程,本文将不会赘述ATMS服务的启动过程,下面来看看AMS是如何启动的。
SystemServer#startBootstrapServices
与ATMS服务类似,AMS服务同样在SystemServer中的startBootstrapServices方法中启动。
frameworks/base/services/java/com/android/server/SystemServer.java
1 |
|
ActivityManagerService#Lifecycle#startService
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
ActivityManagerService.Lifecycle.startService实际上也是通过SystemServiceManager来启动服务,这点与启动ATMS是一致的。
1 |
|
SystemServiceManager#startService
frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
1 |
|
SystemServiceManager的startService接收SystemService对象作为参数,由于AMS本身是继承自AIDL中生成类Stub的,无法继承SystemService,因此其提供一个内部类Lifecycle来继承SystemService。
可以看到与ATMS启动过程是一致的,都是调用SystemServiceManager中同一个startService重载方法来启动服务,最终都是会回调Service类中Lifecycle的onStart方法。
这个过程中将通过反射来创建Lifecycle对象,那么AMS的对象又是什么时候创建的呢?
在AMS.Lifecycle类的构造方法中:
1 |
|
可以看到构造Lifecycle的同时创建了AMS的对象,下面关注下AMS的初始化都干了什么。
ActivityManagerService的初始化
AMS的构造方法代码量比较多,这里删减了一部分。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
1 |
|
可以看到AMS首先创建了两条线程,其中一条作为自己的主要工作线程,接着围绕四大组件的管理做初始化工作。
对于Activity,Android 10中对于它的管理工作全部转移到了ATMS中,因此这里调用了ATMS的initialize方法初始化ATMS。
对于Service,创建了ActiveServices类来管理。在ActiveServices中我们可以看到Service在System Server中对应的实体类为Service Record,并且可以看到Service的超时时间:
1 |
|
对于ContentProvider,创建了ProviderMap类来管理,在System Server中对应数据结构为ContentProviderRecord。
对于广播Broadcast,创建了三个广播队列来分别管理三种广播类型。
除了四大组件,初始中还涉及一些性能相关,例如:
- 初始化ProcessList
- 创建LowMemDetector用于检测低内存
- 创建OomAdjuster用于调整进程的优先级等级
- 创建BatteryStatsService和ProcessStatsService用于管理电量状态和进程状态
- 创建HiddenApiSettings用于管理一些需要对开发者隐藏的HiddenApi
到这里AMS已经初始化完毕,接下来我们继续来看AMS的启动。
AMS.Lifecycle类的onStart方法仅仅是调用了ActivityManagerService#start方法,我们来看看AMS的start方法做了什么。
ActivityManagerService#start
AMS的start方法被调用标志着服务已经启动。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
1 |
|
此方法首先将AMS的LocalService对象添加到本地服务列表中,然后通知其他需要第一时间获取AMS的服务已经可以获取到AMS。
获取AMS服务
上节我们讲述了AMS服务的启动过程,那么如何获取AMS服务呢?
实际上在Activity启动流程一文中,我们已经知道了如何去获取ATMS服务。
而获取AMS服务跟ATMS是类似的,在日常开发中,我们通过context中定义好的方法来获取ActivityManager对象,例如:
1 |
|
ActivityManager是一个和AMS相关联的类,它主要对运行中的Activity进行管理,这些管理工作并不是由ActivityManager来处理的,而是交由AMS来处理。
其内部封装了获取AMS的操作:
1 |
|
可以看到虽然getService方法是public的,但是用@hide标记了,所以我们是无法直接调用到的,不过这不妨碍我们看源码。
重点关注这一行:
1 |
|
Android 10中所有系统服务都通过AIDL接口来获取,通过Stub类的asInterface根据进程的不同获取本地对象或者远程服务的代理对象。
1 |
|
这里采用了静态代理模式将远程服务AMS包装成了Proxy类,所有操作就通过这个Proxy类来完成。
注意:在Android10以前获取服务不是通过直接通过AIDL接口的,而是通过ActivityManagerNative来转发,本质还是通过AIDL生成类Stub来获取。
AMS中重要的数据结构
在Activity启动的一文中简单介绍了关于Activity的几个重要的数据结构,包括ActivityRecord、TaskRecord、ActivityStack等,这一节我们来重点了解一下这几个数据结构。
解析ActivityRecord
我们之前提到ActivityRecord是SystemServer中用来描述Activity的数据类型,其中存储了Activity的所有信息。
ActivityRecord对象在ActivityStarter中创建:
frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
1 |
|
可以看到构造方法中传入的参数很多,其中有一些比较重要,列举在下面表格中。
名 称 | 类 型 | 说 明 |
---|---|---|
mAtmService | ActivityTaskManagerService | ATMS的引用 |
appToken | Token(IBinder) | IBinder对象,用于WMS关联Window与Activity |
launchedFromPackage | String | 启动Activity的包名 |
taskAffinity | String | Activity希望归属的栈 |
intent | Intent | 当前启动Activity的Intent对象 |
aInfo | ActivityInfo | 由AndroidManifest解析出来的Activity信息,包括launchMode、icon、theme、taskAffinity等 |
task | TaskRecord | 当前所属TaskRecord对象 |
appInfo | ApplicationInfo | 当前所属Application信息 |
state | ActivityState | 当前 Activity 的状态 |
supervisor | ActivityStackSupervisor | Android中管理Display与ActivityStack的类 |
icon | int | Activity的图标资源标识符,随ActivityInfo传入 |
theme | int | Activity的主题资源标识符,随ActivityInfo传入 |
从上表我们可以看出ActivityRecord内部存储了由AndroidManifest.xml文件解析出来的Activity配置信息,同时持有了ATMS的引用、Activity当前状态、所属Task等信息。
刚创建时ActivityRecord对象并没有与TaskRecord、ActivityStackSupervisor关联,因为此时并不能确定此Activity所属的任务栈,后续启动Activity的过程中需要确定Activity所属的任务栈,并讲其ActivityRecord对象与TaskRecord相关联。
解析TaskRecord
TaskRecord用来描述一个Activity任务栈,内部也有很多成员变量,同样的挑一部分变量来展示,如下表所示。
名 称 | 类 型 | 说 明 |
---|---|---|
userId | int | 当前进程uid |
taskId | int | 此任务栈唯一的标识 |
affinity | String | 此任务栈的TaskAffinity |
intent | Intent | 启动此Task的Intent |
mActivities | ArrayList<ActivityRecord> | 位于此Task中的所有Activity的ActivityRecord对象 |
mStack | ActivityStack | 当前所处的Stack |
mService | ActivityTaskManagerService | ATMS的引用 |
可以看到,TaskRecord内部存储了任务栈的所有信息,包括任务栈的唯一标识符、任务栈的倾向性、任务栈中的ActivityRecord、ActivityStack和ATMS引用等。
从mActivities集合我们知道TaskRecord与ActivityRecord是包含与被包含的关系,这里TaskRecord又持有一个ActivityStack的引用。
通常我们在开发App中必然会用到启动模式和Flag,其中singlelnstance启动模式或者FLAG_ACTIVITY_NEW_TASK都可以在启动Activity时创建一个新的任务栈。而一个任务栈就对应着一个TaskRecord对象,那么存在一个问题就是如何管理这么多TaskRecord?ActivityStack就是TaskRecord的管理者,下面我们来看看ActivityStack。
解析ActivityStack
ActivityStack从名字上看我们会认为它就是管理Activity的任务栈,实际上通过上节我们知道TaskRecord才是任务栈,而TaskRecord持有ActivityStack,这是否意味着任务栈之上还有一个“活动栈”?我们来看看源码。
ActivityStack中有几个重要的属性:
名 称 | 类 型 | 说 明 |
---|---|---|
mStackId | int | 当前Stack唯一Id |
mDisplayId | int | 当前绑定的Display唯一Id |
mService | ActivityTaskManagerService | ATMS的引用 |
mWindowManager | WindowManagerService | WMS的引用 |
STACK_VISIBILITY_VISIBLE | int | 当前Stack位于栈顶且可见 |
STACK_VISIBILITY_VISIBLE-_BEHIND_TRANSLUCENT | int | 当前Stack可见但上面还有其他的透明Stack |
STACK_VISIBILITY_INVISIBLE | int | 当前Stack完全不可见 |
mTaskHistory | ArrayList<TaskRecord> | 当前Stack管理的所有任务栈 |
mLRUActivities | ArrayList<ActivityRecord> | 当前Stack内的所有Activity |
mPausingActivity | ActivityRecord | 当前Stack中正在暂停的Activity |
mLastPausedActivity | ActivityRecord | 最近暂停的Activity |
mLastNoHistoryActivity | ActivityRecord | 最近不添加到任务栈中的Activity |
mResumedActivity | ActivityRecord | 当前栈顶可见的Activity |
mStackSupervisor | ActivityStackSupervisor | ActivityStackSupervisor引用 |
mRootActivityContainer | RootActivityContainer | RootActivityContainer的引 |
可以看到ActivityStack实际上是作为Activity的管理类存在,其内部维护了Activity 的所有状态、特殊状态的Activity以及和Activity相关的列表等数据。
ActivityStack中还定义了一个内部类ActivityState,其中定义了Activity所处的所有状态:
1 |
|
ActivityStack是由ActivityStackSupervisor来进行管理的,可以看到ActivityStack持有ActivityStackSupervisor的引用。ActivityStackSupervisor引用在ActivityStack的构造器中注入,经过代码搜索,发现ActivityStackSupervisor在ATMS中的initialize方法中创建:
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
1 |
|
initialize方法在AMS的构造方法中被调用,也就是说AMS实例与ActivityStackSupervisor实例是一一对应的,而系统中只会有一个AMS实例,自然也只会存在于一个ActivityStackSupervisor实例。
解析ActivityStackSupervisor
ActivityStackSupervisor是Android系统中Activity的终极大管家,它本身不直接持有ActivityStack的引用,而是通过持有RootActivityContainer间接持有ActivityDisplay引用,进而间接管理ActivityStack。
这里涉及到的类比较多,简单介绍一下:
- RootActivityContainer:Activity容器的根节点,其内部持有mActivityDisplays集合表示系统中所有的ActivityDisplay,Launcher应用的解析、启动都由RootActivityContainer来完成,在Activity启动一文中我们知道通过它可以获取当前持有焦点的ActivityStack和当前栈顶的ActivityRecord对象。
- Display:Android屏幕的抽象,大部分情况下我们的手机只会有一个屏幕,因而只会有一个Display对象。
- ActivityDisplay:内部持有mStacks集合,负责管理当前屏幕内所有的ActivityStack。它由Display对象创建,且它们是一一对应关系,所以我们可以认为ActivityDisplay相当于一个屏幕。
这里同样的列出ActivityStackSupervisor中的重要属性,如下表所示:
名 称 | 类 型 | 说 明 |
---|---|---|
mService | ActivityTaskManagerService | ATMS的引用 |
mRootActivityContainer | RootActivityContainer | RootActivityContainer的引用 |
mRecentTasks | RecentTasks | 最近任务列表 |
mRunningTasks | RunningTasks | 当前运行任务的抽象,可以协助处理运行中的任务 |
mWindowManager | WindowManagerService | WMS的引用 |
mWaitingForActivityVisible | ArrayList<WaitInfo> | 等待Activity显示的进程列表 |
mWaitingActivityLaunched | ArrayList<WaitResult> | 等待Activity启动的进程列表 |
mStoppingActivities | ArrayList<ActivityRecord> | 处于Stopping状态的Activity列表 |
mFinishingActivities | ArrayList<ActivityRecord> | 处于Finishing状态的Activity列表 |
mGoingToSleepActivities | ArrayList<ActivityRecord> | 准备休眠的Activity列表 |
mMultiWindowMode-ChangedActivities | ArrayList<ActivityRecord> | 多窗口模式中的Activity列表 |
mPipModeChangedActivities | ArrayList<ActivityRecord> | 画中画模式中的Activity列表 |
mNoAnimActivities | ArrayList<ActivityRecord> | 不考虑转场动画的 Activity列表 |
mTopResumedActivity | ActivityRecord | 系统中最顶部可见的Activity |
可以看到与ActivityStack维护栈内的特殊Activity类似,ActivityStackSupervisor负责维护整个系统中的所有特殊状态的Activity,同时”最近任务“功能也跟它有关。
下面我们再来看下ActivityDisplay。
解析ActivityDisplay
ActivityDisplay是Android系统中屏幕的抽象,对于手机来说,一般情况下系统中只会有一个ActivityDisplay的实例。
ActivityDisplay负责管理ActivityStack,其内部持有多个ActivityStack对象,其中包括一些特殊的ActivityStack例如Launcher对应的Stack、最近任务对应的Stack等。下面我们来看看其中的重要属性。
名 称 | 类 型 | 说 明 |
---|---|---|
mDisplayId | int | 当前绑定的Display唯一ID |
mDisplay | Display | 当前绑定的Display |
mStacks | ArrayList<ActivityStack> | 当前Display中所有的ActivityStack |
mPreferredToFocusableStack | ActivityStack | 当前聚集、可见、顶部的ActivityStack |
mLastFocusedStack | ActivityStack | 最近聚焦的ActivityStack,如果栈顶的ActivityStack已经resume的话(mPreferredToFocusableStack不为空),与mPreferredToFocusableStack为同一。 |
mHomeStack | ActivityStack | Launcher应用的ActivityStack |
mRecentsStack | ActivityStack | 最近任务的ActivityStack |
mPinnedStack | ActivityStack | 多窗口下的ActivityStack |
mSplitScreenPrimaryStack | ActivityStack | 分屏模式下的主ActivityStack |
mService | ActivityTaskManagerService | ATMS的引用 |
mRootActivityContainer | RootActivityContainer | RootActivityContainer的引用 |
可以看到ActivityDisplay除了维护Launcher应用所处的ActivityStack之外,还维护了一系列特殊的ActivityStack。
Activity栈管理
我们平时开发时经常有需求将某些Activity置于单独的任务栈中,例如手机的拨号界面,我们希望它只有一个实例并且处于单独的任务栈,因此会使用singleInstance的启动模式来启动它。
那么Android系统是如何管理这些任务栈的呢?下面我们就来理一理Activity的栈管理机制。
Activity任务栈模型
Activity任务栈并不是凭空想象出来的,它是由多种数据结构共同组合而成。
上一节我们介绍了许多数据结构,其中ActivityRecord、TaskRecord和ActivityStack共同组成了下图的Activity任务栈模型。
其中ActivityRecord用来记录一个Activity的所有信息,TaskRecord用来管理栈中的ActivityRecord,它就是我们常说的任务栈,其中包含了多个ActivityRecord,一个App中会有多个任务栈。ActivityStack又包含了一个或者多个TaskRecord,它是TaskRecord的管理者。
Activity栈管理是建立在Activity任务栈模型之上的,有了栈管理我们可以对Activity进行缓存复用,使得一个App可以有多个任务栈而不至于返回栈混乱,必要的时候我们还可以在启动其他Activity时将其任务栈置于当前App的顶部而达到跨进程的效果。
举个例子,我们可以在App中通过隐式Intent的方式调起拨号界面,如下图所示。
系统的拨号App的launchMode通常是singleInstance,使用singleInstance启动的Activity会有一个单独的任务栈,并且任务栈中只有一个Activity。当我们通过隐式Intent启动拨号Activity时,拨号界面对应的Task将会覆盖到当前App任务栈的栈顶,这使得我们可以进行跨任务栈的操作。
为了更灵活的进行栈管理,Android系统中提供了很多配置,包括LaunchMode、Intent Flag、taskAffinity、allowTaskReparenting等,下面来重点了解一下这几个机制。
Launch Mode
Launch Mode大家都不陌生,用于设定Activity的启动模式,无论是哪种启动模式所启动的Activity都会位于当前Activity栈的栈顶,Android中有4种LaunchMode。
- standard:默认启动模式,每次启动Activity都会创建一个新的Activity实例。
- singleTop:如果要启动的Activity已经存在栈顶,则不会重新创建此Activity实例,而是会回调其onNewIntent方法将新的Intent传递。如果不在栈顶则会重新创建Activity实例。
- singleTask:如果要启动的Activity已经存在并且位于其taskAffinity对应的Task中,则不会重新创建此Activity,而是将此Task中位于此Activity上方的所有Activity出栈使得要启动的Activity重新回到栈顶,并且回调其onNewIntent方法。如果Activity设置的taskAffinity对应的Task中没有此Activity实例或者此Task不存在的话,会创建Activity实例。此种模式下启动的Activity为全局单例。
- singleInstance:与singleTask类似,区别是singleInstance启动的Activity会独占一个任务栈,因为不会有清顶问题。此种模式下启动的Activity同样也是全局单例。
关于启动模式这块其实都是老生常谈了,即时是刚入门的开发者也熟悉这些启动模式的特性,不过在本篇文章中我们希望能在Activity栈管理的角度来更加深入的了解启动模式。
standard模式的多实例特性
上面我们提到standard启动模式的特性是“每次启动Activity都会创建一个新的Activity实例”,对于这个启动模式,网络上大部分的文章都是局限于单一的任务栈来分析。实际上我们应该更着重于其在Task与Task之间的逻辑。
举个例子,当我们在短信App中点击一个电话号码的超链接,选择新建联系人时,就会以standard模式启动新建联系人的Activity,此Activity由通信录App提供。
由于我们是使用standard模式启动的Activity,因此会新建一个Activity实例,并添加到当前短信App栈顶的任务栈中,如下图所示。
这种情况下启动的新建联系人Activity将会直接加入到当前任务栈的栈顶,此时该Activity只与短信App的任务栈Task具有相关性,并不会影响到原有的通讯录App任务栈。
当我们点击返回按键时,新建联系人Activity出栈,回到短信详情页,我们依旧处于短信App任务栈中。
singleTask任务栈切换
不同于standard模式启动的Activity会直接加入到当前任务栈栈顶,singleTask模式启动的Activity只会存在于特定任务栈中,这个任务栈与其设置的taskAffinity有关。
对于没有指定taskAffinity的Activity,其taskAffinity等于当前包名,这意味着如果我们在以singleTask启动一个当前App内的未指定taskAffinity的Activity,将不会发生任务栈的切换。
这里我们主要讨论启动其它App中的,或者指定了taskAffinity的Activity,这种情况下启动的Activity没有办法入栈的话,如何让它显示在栈顶呢?
只需要把此Activity所在的任务栈整体移到前台即可。不过此任务栈的栈顶可能并不是我们启动的Activity,这种时候就会进行一次“清顶”操作,将要启动的Activity上方所有Activity出栈,并且不会重建当前Activity。
上图展示了一次singleTask模式启动Activity导致的任务栈切换,实际操作中会有较为明显的Task切换动画,而不是Activity切换动画。
图中展示的是目标Activity所属任务栈已经存在的情况,如果启动目标Activity时其归属的任务栈还未启动,那么会先创建对应的任务栈TaskRecord。
另外需要注意的是,目标Activity的回退栈可能还存在其它Activity,例如图中的“邮件首页”,此时会随着任务栈的切换一并移动到当前任务栈的上方,并且当我们按返回键时不会立刻回到App中而是先回到邮件首页。
使用singleInstance启动Activity时Task的表现与非本栈singleTask启动或singleTask+taskAffinity类似,区别在于是由于singleInstance的任务栈只会有其一个Activity,所以当我们返回时一定返回到原App Task中。
Intent Flag
在Intent中定义了很多Flag,其中有几个Flag直接影响了Activity栈的管理,需要注意的是如果Launch Mode与Intent Flag有冲突的话以Intent Flag为准。
下面介绍一些与栈管理有关的Flag。
名 称 | 效 果 |
---|---|
FLAG_ACTIVITY_SINGLE_TOP | 与Launch Mode中singleTop效果一致 |
FLAG_ACTIVITY_NEW_TASK | 以目标Activity为根Activity,为目标Activity启动一个新的任务栈,如果目标任务栈存在,则复用。Launch Mode中singleTask和singleInstance都会自动添加这个Flag |
FLAG_ACTIVITY_CLEAR_TOP | 类似于singleTop中的“清顶”效果,启动时将会清除回退栈上方的Activity,使其位于栈顶可见 |
FLAG_ACTIVITY_NO_HISTORY | 此Activity不会加入到回退栈中,这意味着一旦从这个Activity切走,这个Activity就会被finish,因此它没有办法响应onActivityResult回调 |
FLAG_ACTIVITY_MULTIPLE_TASK | 为Activity创建新的任务栈,并且不会搜寻现有的任务栈,需要与FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_NEW_DOCUMENT一起使用才有效果 |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Activity不会加入到“最近启动的活动”列表中 |
FLAG_ACTIVITY_BROUGHT_TO_FRONT | 当启动模式为singleTask时此Flag被系统设置,表示将Task转移到前台 |
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | 表示Activity由“最近任务”中启动而来 |
FLAG_ACTIVITY_CLEAR_TASK | 清除目标Activity对应Task中其它所有Activity,需要与FLAG_ACTIVITY_NEW_TASK配合使用 |
这些Flag在何时被解析呢?
同样的在Framework系列的第二篇Activity启动流程一文中,ActivityStarter的startActivityUnchecked()中调用了computeLaunchingTaskFlags()方法来计算Intent Flag:
frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
1 |
|
首先在setInitialState()方法中会初始化启动Activity的各种配置,包括启动的来源ActivityRecord、Intent、ActivityOptions、目标Activity需要入栈的TaskRecord和LaunchFlags等。
接着在computeLaunchingTaskFlags()方法会计算出Activity启动的LaunchFlags,计算后将其复制给成员变量mLaunchFlags,我们来重点关注这个方法。
1 |
|
需要注意的是代码中SourceRecord表示发起启动请求的源Activity,如果我们不是从Activity的Context来启动的,那么它就会为空。mInTask表示待启动的Activity应该加入的任务栈。
注释已经相当详细了,我们主要关注mInTask为null的情况,也就是最后的那段代码。
当 mInTask == null 为true时,有三种情况:
mSourceRecord为空。
当mSourceRecord为空时,说明我们并不是由Activity的Context发起的startActivity请求,此时如果Intent中没有添加 FLAG_ACTIVITY_NEW_TASK 的话将会自动添加,在旧版Android中会直接报错。
mSourceRecord不为空,且Source Activity的启动模式为singleInstance。
这种情况表示我们从一个launch mode为singleInstance的Activity启动另一个Activity,由于其独占一个任务栈的特性,不能直接加入到Source Activity的Task中,而是要创建新的任务栈(或者复用另外的任务栈),那么会添加FLAG_ACTIVITY_NEW_TASK。
mSourceRecord不为空,Source Activity的启动模式不为singleInstance,且目标Activity的启动模式为singleTask或者singleInstance。
这种情况也是不能直接添加到Source Activity所在任务栈,需要创建新栈或者复用另外的栈,那么会添加FLAG_ACTIVITY_NEW_TASK。
taskAffinity和allowTaskReparenting
我们可以在 AndroidManifest.xml 中设置 android:taskAffinity,用来指定Activity希望归属的栈,在默认情况下,同一个应用程序的所有的Activity都有着相同的taskAffinity,值为App的包名。
在之前介绍相关数据结构时,我们可以看到TaskRecord中有一个affinity属性,它就是这个Task的taskAffinity属性。Task中的affinity属性会在其创建的时候被赋值,创建的时候其实就是它的Root Activity启动的时候,也就是说Task的affinity属性就是其栈中根Activity的taskAffinity属性。
taskAffinity属性在下面两种情况时会生效:
taskAffinity与singleTask或者FLAG_ACTIVITY_NEW_TASK配合。
使用singleTask模式启动或者设置了FLAG_ACTIVITY_NEW_TASK标志的Activity启动时将会通过其taskAffinity寻找其目标任务栈,如果此栈存在将会入栈,如果不存在则新建Task。
注意:taskAffinity也可以影响到singleInstance启动的Activity的任务栈,但没有意义。
taskAffinity与allowTaskReparenting配合。
我们可以在AndroidManifest.xml为Activity配置android:allowTaskReparenting属性,表示允许此Activity更换其从属的任务栈。设置此属性的Activity一但当前Task切换到了后台,就会回到它“倾向”的任务栈中。如何理解这句话呢?我们依旧以新建联系人为例,如下图所示。
可以看到当我们启动了设置了allowTaskReparenting为true的Activity时,如果我们按下最近任务键,此Activity将会回到与其taskAffinity更相近的通讯录App的Task中,重新回到短信App时栈顶的Activity已经变成了短信详情页,而此时打开通讯录App会发现原先在首页的App已经在新建联系人页了。
下面我们来看看源码中taskAffinity是如何应用的。
同样的,在ActivityStarter的startActivityUnchecked方法中:
frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
1 |
|
调用了本类中的getReusableIntentActivity方法来寻找是否有可以复用的Activity。此方法会调用RootActivityContainer的findTask来寻找对应的Task,经过层层调用最终会调用到Task的管理者ActivityStack的findTaskLocked中,我们来看看相关源码:
frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
1 |
|
此方法源码比较长,这里只截取了关键的部分,可以看到ActivityStack遍历了其管理的现存所有TaskRecord,如果task的rootAffinity属性与目标Activity的taskAffinity相同,那么将栈顶的Activity保存到结果中,表示我们已经找到了匹配的任务栈。
总结
本篇文章以Android 应用开发工程师的角度简单的介绍了Activity Manager Service服务,以及其相关的数据结构、Activity栈管理等。
由于AMS家族相关的源码相当浩瀚,要想一窥全貌是相当困难的,更好的选择是当我们需要去了解某部分功能时再去源码中寻找会比较有方向。
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!