背景
在上一篇Android Activity启动过程(一)中,我们的探究过程截止到了通过 Binder 机制向 ActivityManagerService(以下简称AMS) 发出 startActivity 请求,本节,我们将接着讨论 AMS 对 start 请求的处理过程。
使用源码:Nougat - 7.0.0_r1
StartActivity(二)
基础结构
首先,我们从管理的角度,看一看AMS管理 Activity 的基础结构。
这里,我们需要清除几个概念:
- ActivityStackSupervisor:该类的实例化对象存在于 AMS 中。它负责管理所有的 ActivityStack。
- ActivityStack:于 StackId 一一对应,SstackId是有限的,包括如下几种,这也就意味了ActivityStack的个数就只有以下几种。每个ActivityStack对应着不同的使用模式。比如DOCKED_STACK_ID 对应着分屏模式,通常我们使用的模式是FULLSCREEN_WORKSPACE_STACK_ID 。
/** First static stack ID. */
public static final int FIRST_STATIC_STACK_ID = 0;
/** Home activity stack ID. */
public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
/** ID of stack where fullscreen activities are normally launched into. */
public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
/** ID of stack where freeform/resized activities are normally launched into. */
public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
/** ID of stack that occupies a dedicated region of the screen. */
public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
/** ID of stack that always on top (always visible) when it exist. */
public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
/** Last static stack stack ID. */
public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
/** Start of ID range used by stacks that are created dynamically. */
public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
- mTaskHistory:它是一个ArrayList结构的,实际上AMS内部维护了很多ArrayList的集合分别对各任务进行管理。比如在mTaskHistory中,记录了该模式下所有的TaskRecord。当某一个Activity被创建,将进入相应的TaskRecord,当该Activity被销毁,则从TaskRecord中remove,同时从TaskRecord栈顶再取出一个Activity显示。
- TaskRecord:从上面的描述中,其实我们已经看到了TaskRecord的主要作用,即作为一个任务的集合。一个任务中可以包含很多的Activity。
- ActivityRecord:是AMS中对一个Activity的描述,可以说,它代表了一个实际的Activity。而在TaskRecord中,实际上存储的就是这个ActivityRecord。
下面,我们通过ActivityStack的产生构建过程,进一步认识一下它们之间的关系:
执行过程
在AMS执行过程关键步骤的描述如下图所示:
上述过程中,主要完成了一下工作:
- 验证要启动的Activity的合法性;
- 生成Activity所对应的 ActivityRecord,同时根据Activity的启动方式的不同,将ActivityRecord放入合适的 TaskRecord中。即建立好 ActivityRecord---TaskRecord---ActivityStack之间的关系。其实不仅仅是这些关系,为了方便管理,还有一些其他关系需要被建立;
- 将 ActivityRecord 置顶在 TaskRecord中,将TaskRecord置顶在mTaskHistory中;这样就为显示该Activity做好了足够的准备;
- 最后通过IBinder发送消息到调用 startActivity 的源头(比如Launcher),告诉它可以进行pause状态了。
做好以后工作,ok,下面就回到了源头所在的进程中,接着执行,回到源Activity进入pause,这样做的主要目的,第一,让源Activity 执行它的回调方法 onPause;第二,保存一些必要的数据,使得在此回到这个Activity时,能够回到现有的状态。这个执行过程如下:
首先,我们来看看源Activity主要做了哪些处理:
...
if (userLeaving) {
performUserLeavingActivity(r);
}
...
//执行onPause回调
performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
...
//保存必要数据
QueuedWork.waitToFinish();
QueuedWork.waitToFinish()源码如下:
/**
* Finishes or waits for async operations to complete.
* (e.g. SharedPreferences$Editor#startCommit writes)
*
* Is called from the Activity base class's onPause(), after
* BroadcastReceiver's onReceive, after Service command handling,
* etc. (so async work is never lost)
*/
public static void waitToFinish() {
Runnable toFinish;
while ((toFinish = sPendingWorkFinishers.poll()) != null) {
toFinish.run();
}
}
从上图可以看到,此时角色发生了反转。AMS作为客户端,源Activity进程作为服务提供者。CS结构中C和S的角色总是相对的。当ActivityThread完成 pause 工作之后,告知AMS进行下一步工作。
现在,让我们回到AMS中,接着看。接下来的主要步骤如下:
主要工作主要包括,处理来自源进程中 Activity 已经 pause 的消息;从 focused stack找到 top activity,检测到该activity没有所属的进程,因此回到 AMS 中,启动新进程。
所有已经启动的process将以ProcessRecord的形式存储在AMS中的ProcessMap属性中,获取一个已启动ProcessRecord过程如下:
public E get(String name, int uid) {
SparseArray uids = mMap.get(name);
if (uids == null) return null;
return uids.get(uid);
}
由此可见,所有的Activity都运行在一个process中,而启动的process是有 uid 和进程名唯一确定的。uid是Activity的用户id,安装的时候由PackageManagerService确定,进程名是在manifest.xml中自己对Activity配置的,默认为包名。决定是否需要创建新进程的调用过程是在startSpecificActivityLocked进行的。因此进程是在需要的时候才被创建的,而是否需要的标准,就是根据这个uid和进程名称是否能找到相应的进程。
创建新进程的过程在startProcessLocked中完成:
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
Process.start方法如下:
/**
* Start a new process.
*
* If processes are enabled, a new process is created and the
* static main() function of a processClass is executed there.
* The process will continue running after this function returns.
*
*
If processes are not enabled, a new thread in the caller's
* process is created and main() of processClass called there.
*
*
The niceName parameter, if not an empty string, is a custom name to
* give to the process instead of using processClass. This allows you to
* make easily identifyable processes even if you are using the same base
* processClass to start them.
*
* @param processClass The class to use as the process's main entry
* point.
* @param niceName A more readable name to use for the process.
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
* @param abi non-null the ABI this app should be started with.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
//这里,启动了由“受精卵”启动了新的进程
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
小结
本节主要描述了Activity启动在AMS中所需要做的工作。
我们看到,其中有大量的数据结构,维持着Activity正确的启动、不同状态之间的切换。同时,对于数据结构+算法的认知,又更加进了一步。
下一节,接着完成最后的剩下的调用执行过程。
第一次阅读Activity启动的源码,同时查了很多资料。这是一个不断剥茧抽丝的过程,也许会有疏漏,还请指正。