对WMS很感兴趣,所以决定以在桌面点击应用图标,到应用的Activity显示到屏幕上,这一简单操作为基础,分析整个过程。
其中涉及到非常多的模块,但是首先需要分析的就是Activity的启动流程,由于篇幅原因,分为以下3部分:
【Android 13源码分析】Activity启动流程-1
【Android 13源码分析】Activity启动流程-2
【Android 13源码分析】Activity启动流程-3
虽然整个操作实际上就2S时间,但是整个完整的流程分析下来也需要几个月,而且还是仅仅是启动主流程,
这三篇对Activity启动流程分析只关心主流程,不看具体细节,当然对于一些关键方法会着重介绍,这样以后如果有遇到相关问题的修改可以通过这篇笔记找到具体代码位置,然后根据具体的问题分析修改。
后续会有在Activity启动流程基础上延伸出来的各个分支的记录,比如窗口的创建与挂载,Surface相关,窗口的动画等待,这些流程的分析都需要基于Activity启动流程。
启动Activity的方式有很多,当前以在Launch中点击“电话”图标启动应用为例,本篇为第二篇。
接【Android 13源码分析】Activity启动流程-1在ActivityStarer::startActivityInner方法中,通过getOrCreateRootTask方法创建Task,而后通过setNewTask方法将新建的ActivityRecord进行挂在到新建的Task上,接下来将需要处理新的Activity启动和显示逻辑。
后续的流程由resumeFocusedTasksTopActivities执行。
显示Activity resumeFocusedTasksTopActivities
调用链
1 | arduino复制代码RootWindowContainer::resumeFocusedTasksTopActivities |
主流程跟踪
经过几次调用会执行到TaskFragment::resumeTopActivity方法,这个方法非常复杂,场景不同执行的分支也不同,值得重点分析。而且很多重要的逻辑都集中在这里,我觉得后续会被重构的。
当前还是只分析launcher 启动“电话”的场景
1 | java复制代码# TaskFragment |
重点分析:
上面说过这个函数非常复杂,在当前逻辑有2个主线
- pause当前Activity,也就是launcher
- 异步创建“电话”的进程
在第一步将launcher的Activity执行pauser, 这一步执行到最后也会触发”电话”应用MainActivity的启动,第二步创建“电话”进程,进程创建完肯定也会执行”电话”应用MainActivity的启动,这么看来就有2个地方触发了。
这是因为是异步创建进程,不知道谁先执行完,但是可以明确的是,”电话”应用MainActivity必须有2个条件:
- 前一个Activity执行完pause
- 进程创建完成
所以无论2个分支哪一个先执行完,都需要等后一个执行完的来触发后续电话”应用MainActivity的启动。
先看launcher的pause流程
需要显示新的Activity,那么之前的肯定是要执行pause的,就在这里执行。参数next为“电话的”ActivityRecord
1 | java复制代码# TaskDisplayArea |
forAllLeafTasks和forAllLeafTaskFragments在【WMS/AMS 常见方法调用提取】中有解释,那么当前这段方法其实就是让DefaultTaskDisplayArea下的每个叶子LeafTaskFragments执行startPausing。
1 | typescript复制代码# TaskFragment |
最终在TaskFragment::schedulePauseActivity构建并执行了launcher的pause事件。
这块相关的日志输入如图:
因为是后面补充的所以对象不一样,但是能确定这里处理的TaskFragment和mResumedActivity都是launcher对象的。
tip:
State movement这段的输出是在ActivityRecord::setState 只有状态改变都会输出
接下来的重点是看PauseActivityItem的执行逻辑
1.1 PauseActivityItem
1 | typescript复制代码# PauseActivityItem |
默认已经知道ClientTransaction调用逻辑,不知道移步【】
这里先执行execute 的逻辑,也就是 launcher的pause流程,这个不是主线,单独解释【】。然后执行postExecute。这里会触发新Activity的启动。
1 | scss复制代码# ActivityClient |
getActivityClientController返回的是ActivityClientController对象。
调用链
1 | arduino复制代码ActivityClientController::activityPaused |
调用堆栈如下:
不是每个函数都要去跟,没有意义,跟踪重点函数,关注重点处理的地方即可。 否则下个版本重构后,就完全不一样。 所以只需要记住重点做的事情就好。
这边又2个地方都会出发ActivityTaskSupervisor::startSpecificActivity,而且都是由ActivityClientController::activityPaused为源头。
由activityPaused方法名也能知道这段逻辑是在launcher 执行完pause后再执行的
注意这里在第一个分支 RootWindowContainer::resumeFocusedTasksTopActivities到TaskFragment::resumeTopActivity直接的调用栈和上一篇是一样的,只不过最后执行的不一样而已
pause后,虽然又2个分支,但是根据函数名,其实都是为了能让下一个Activity可见。毕竟不能让用户什么都看不见, 总得显示一个吧
主流程跟踪
接下来进入代码分析
1 | typescript复制代码# ActivityRecord |
结合前面TaskFragment::resumeTopActivity的创建进程,然后根据堆栈知道这2个分支也也创建新进程,那已知3地方触发创建进程。
这里的 topRootTask 就是SourceActivity“电话”的Activity,prev是launcher的,resuming为null。
1.1.1 分支1 resumeFocusedTasksTopActivities
1 | arduino复制代码# TaskFragment |
又是TaskFragment::resumeTopActivity方法,这次是直接走到这个方法的最下面,启动Activity。
1.1.2 ensureActivitiesVisible分支
看方法名是为了 Activity的可见
根据上面的调用链,会执行到 DisplayContent::ensureActivitiesVisible
1 | scss复制代码# DisplayContent |
这里拿出来主要是遇到了forAllRootTasks这个方法,其实就和之前的forAllLeafTasks使用方式是一样的。对每个rootTask执行Lambda而已。
1 | java复制代码# Task |
这里又有个forAllLeafTasks,调用每个叶子Task的updateActivityVisibilities方法,但是Task的这个方法是气父类TaskFragmet里定义的,所以看TaskFragmet。
1 | java复制代码# TaskFragmet |
我们知道这一条线都是为了处理Activity可见的。 在这定义了一个专门的类来处理。
不过需要注意的是,这个方法会执行多次,因为他是遍历每一个符合条件的子容器,从上到下遍历。
1.1.3 EnsureActivitiesVisibleHelper::process
1 | java复制代码# EnsureActivitiesVisibleHelper |
这里的r 表示需要启动的ActivityRecord,当前流程还是onPause的流程。并没有知道创建进程那一步,所以进程是没有attached的。那就会执行makeVisibleAndRestartIfNeeded
1 | arduino复制代码# EnsureActivitiesVisibleHelper |
1.1.4 ActivityTaskSupervisor::startSpecificActivity
这个方法也被调用了2次
1 | java复制代码# ActivityTaskSupervisor |
这里有2个分支,如果目标进程创建了,则走realStartActivityLocked,否则执行创建的逻辑。不过就目前2个调用的地方都是执行onPause的逻辑性,这里还是其实都是制作到startProcessAsync。 而不会走进realStartActivityLocked
2 创建进程逻辑
这里是回到执行pause的同级流程,也就是最前面的TaskFragment::resumeTopActivity
执行pause后会执行 ActivityTaskManagerService::startProcessAsync,最后也是通过 ActivityManagerService来触发启动进程的。
看看AMS这块执行进程创建的调用流程
1 | java复制代码# ATMS |
通过 startProcess 来启动应用进程
执行完后输出日志
1 | ruby复制代码07-26 19:19:05.477 8737 8782 I ActivityManager: Start proc 19643:com.example.myapplication/u0a198 for next-top-activity {com.example.myapplication/com.example.myapplication.MainActivity} |
这段日志是在 ProcessList::handleProcessStart 方法里构建了个StringBuilder,然后传递给AMS::reportUidInfoMessageLocked进行打印的。
本文转载自: 掘金