对WMS很感兴趣,所以决定以在桌面点击应用图标,到应用的Activity显示到屏幕上,这一简单操作为基础,分析整个过程。
其中涉及到非常多的模块,但是首先需要分析的就是Activity的启动流程,由于篇幅原因,分为以下3部分:
【Android 13源码分析】Activity启动流程-1
【Android 13源码分析】Activity启动流程-2
【Android 13源码分析】Activity启动流程-3
虽然整个操作实际上就2S时间,但是整个完整的流程分析下来也需要几个月,而且还是仅仅是启动主流程,
这三篇对Activity启动流程分析只关心主流程,不看具体细节,当然对于一些关键方法会着重介绍,这样以后如果有遇到相关问题的修改可以通过这篇笔记找到具体代码位置,然后根据具体的问题分析修改。
后续会有在Activity启动流程基础上延伸出来的各个分支的记录,比如窗口的创建与挂载,Surface相关,窗口的动画等待,这些流程的分析都需要基于Activity启动流程。
启动Activity的方式有很多,当前以在Launch中点击“电话”图标启动应用为例。
模块框图
一级框图
对于Activity的启动流程来说,可以分为3个模块:
SourceActivity:执行startActivity方法的Activity,发起请求的Activity,当前就是Launch的Activity
TargetActivity:需要被启动的Activity,当前就是“电话”应用在清单文件配置的MainActivity
AMS: 不仅仅是指AMS这一个类,而是指处理整个启动流程的管理类。
举个例子, 以在公司的工作流程来说, launch模块的开发,在处理一个bug,但是涉及到了通话,那么他肯定是需要找到通讯组的同事来处理这个问题。 但是公司很大,他并不知道通讯模块是谁负责,更不知道这个问题需要交给通讯组具体的哪个同事处理,那么他只需要将自己的要求向公司领导(管理)汇报:需要通讯组的同事处理这个问题。
当前例子设计到launcher和通讯2个模块的开发人员,还涉及到公司的管理者。在Activity启动也是如此, 对于sourceActivity、targetActivity他们并不知道直接作用对方的行为,所以这一流程需要AMS来做管理。
并且,这3个模块也对应3个进程,当前案例来说分别为:launch进程,电话进程,system_service进程。
这里AMS对launch多了一个返回箭头的原因是因为launch肯定是需要执行pause的,但是整个启动流程非常复杂,执行pause的时机launch自身无法控制,只能由AMS控制。
二级框图
这里有3个颜色,代表3个阶段。
第一阶段:
- 由launcher 进程发起启动Activity的请求
- AMS处理,创建对应的ActivityRecord和Task,并挂载到窗口层级树中
第二阶段:
- AMS 触发launcher 的pause流程
- AMS 触发”电话”应用进程的创建
- launcher执行pause流程
- launcher执行完pause后执行调用AMS的startSpecificActivity方法启动“电话”Activity
ASM 通知 launcher 执行pause和通过 Zygote 创建进程都是异步的,不知道执行的顺序。所以launcher执行completePause后调用启动Activity时,会判断进程是否已经创建完毕。
第三阶段:
- “电话”进程创建完毕,通知AMS
- AMS触发realStartActivityLocked,通知应用启动Activity
- 应用进程执行Activity的启动流程,生命周期 走到onCreate和onResume
这里第二步,里面会判断launcher是否执行完pause了, 如果没有执行则直接return。
也就是说需要启动“电话”的Activity,必须有2个条件:1. 进程创建完毕 2. launcher执行完pause
在新的Activity显示前, launch肯定是要执行pause的。
这一阶段调用比较简单,堆栈如下:
其实我们正常通过 startActivity 传递 intent 启动Activity的流程也是一样的。 最终都会调到Instrumentation::execStartActivity。然后开始跨进程与AMS通信。
调用链
Activity::startActivity
1 | arduino复制代码 Activity::startActivity |
主流程跟踪
在发起启动Activity的这个应用端,逻辑相对简单,无论哪种参数的 startActivity 方法,最终都是调到 startActivityForResult 方法。
然后在 Instrumentation 最后的处理,然后开始跨进程传递到 system_service 进程中
1 | less复制代码# Activity |
checkStartActivityResult 这个方法,比如常见的未在AndroidManifest.xml注册Activity的报错就在这。
1 | java复制代码# Instrumentation |
调用链
1 | arduino复制代码ActivityTaskManagerService::startActivity |
主流程跟踪
流程来到ActivityTaskManagerService::startActivity,经过2次简单的跳转会执行 startActivityAsUser 方法。
这个方法比较重要,在这里会构建一个 ActivityStartController ,根据类名可以知道这个类是控制Activity启动。
代码如下:
1 | scss复制代码 private int startActivityAsUser(IApplicationThread caller, String callingPackage, |
ActivityStartController::obtainStarter返回的是ActivityStarter对象
1 | scss复制代码# ActivityStartController |
所以在ActivityTaskManagerService::startActivityAsUser方法中的build模式,其实是对 ActivityStarter 对象做构建。最终调用其 execute 方法。
然后调用 executeRequest 方法。
1 | csharp复制代码# ActivityStarter |
2.1创建ActivityRecord
ActivityStarter::executeRequest是一个需要注意的方法,因为内部会创建 ActivityRecord 对象,而这个 ActivityRecord 对象持有 token ,这个token就是以后分析其他逻辑一直会出现的token。
应用进程中的Activity在AMS的代表就是ActivityRecord
1 | scss复制代码# ActivityStarter |
而 startActivityUnchecked 主要调用的是 startActivityInner , 这个方法是流程的关键点。
2.2 关键函数startActivityInner
这个函数是AMS在Activity启动流程最重要的函数之一,这里涉及到【层级结构树】相关,先看看正常在launch的和启动“电话”后的层级树对比。
这里首先多了3个东西:1个Task,1个上一步创建的ActivityRecord,还有一个就是WindowState。
另外这个Task还移动到了DefaultTaskDisplayArea的最顶部。这里涉及到的操作如下:
- 创建Task
- ActivityRecord挂在到这个Task下
- 将这个Task移动到最上面
至于最下面的那个 546fce2 为什么是WindowState对象,又是怎么挂在到ActivityRecord上的,这个在【应用的addWindow流程】详细说明了。
1 | java复制代码# ActivityStarter |
具体原因在代码加了注释,那么第一步就是需要创建一个Task了。
2.2.1 创建Task getOrCreateRootTask
调用链
1 | arduino复制代码ActivityStarter::getOrCreateRootTask |
主流程代码
1 | less复制代码# ActivityStarter |
小结:
最后描述一下最后创建的2个重点部分:
- 看到通过buildInner 创建了一个task,而buildInner 也很简单粗暴,通过各个变量直接new Task 对象。
- mParent 不为null, 是 因为在创建的时候 setParent(this),当前的这个this,就是 getDefaultTaskDisplayArea返回的。就是 37层的第二层应用Activity存在的”DefaultTaskDisplayArea”。
在 RootWindowContainer::getOrCreateRootTask 体现。
注意log里的 #17 的这个Task,与前面的层级结构树新增的Task,是对应的上的。而且this= DefaultTaskDisplayArea 说明也确实是往DefaultTaskDisplayArea里添加了。
另外 log里index为3,结合最上面的前后对比,说明也的往顶部添加。
2.2.2 将task与activityRecord setNewTask
调用链
1 | arduino复制代码 ActivityStarer::setNewTask |
主流程代码
1 | java复制代码# ActivityStarer |
这里的task 和mTargetRootTask是同一个对象, 进源码跟到流程也是一样。
然后进入 addOrReparentStartingActivity
1 | less复制代码# ActivityStarer |
这里的逻辑设计到的Task就是上一步创建的Task,mStartActivity则是“电话”在之前逻辑创建的ActivityRecord。
setNewTask的堆栈信息如下
另外这段逻辑里有个ProtoLog打印,日志如下:
小结:
结合逻辑分析+堆栈信息+ProtoLog,可以确认setNewTask做的事情就是将ActivityRecord挂在到Task中,而且在顶部
2.2.2.3 移动Task到容器顶部 moveToFront
这里提一下这个moveToFront方法,因为前面创建Task并添加到 DefaultTaskDisplayArea 时是往顶部添加,后面将ActivityRecord挂在到Task,也是挂在到其顶部。所以这个函数其实没有什么实际操作。但是对于其他场景,这里也是一个重点方法。
调用链
1 | arduino复制代码Task::moveToFront -- 2.1.3 移动Task |
主流程代码
首先确定一个问题。 需要移动到顶部的是哪个task? 这个task所在的是在哪个task? 这里设计到了2个task
在ActivityStarter::startActivityInner的时候调用的是这段代码
1 | scss复制代码mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask); |
已知mTargetRootTask是新创建给“电话”用的Task, mTargetRootTask.getRootTask()肯定就是还是本身。
tip :getRootTask返回的是顶部的Task, 当天Task上一层是TaskDisplayArea类型 (name为DefaultTaskDisplayArea)
而mTargetRootTask.getParent返回的父容器,则是 name为DefaultTaskDisplayArea的TaskDisplayArea。
具体不深入,以主流程为主.。
到这里,AMS已经将需要的ActivityRecord和Task创建并且挂载到层级树中接下来将是需要处理新的Activity启动和显示逻辑了
2.2.3 显示Activity resumeFocusedTasksTopActivities
篇幅原因,后续流程在下一篇
本文转载自: 掘金