作者:努比亚技术团队
源码来源:努比亚技术团队
1.前言
从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面而完成应用启动,快的话往往都不需要一秒钟,但是这整个过程却是十分复杂的,其中涉及了Android系统的几乎所有核心知识点。同时应用的启动速度也绝对是系统的核心用户体验指标之一,多少年来,无论是谷歌或是手机系统厂商们还是各个Android应用开发者,都在为实现应用打开速度更快一点的目标而不断努力。但是要想真正做好应用启动速度优化这件事情,我想是必须要对应用启动的整个流程有充分的认识和理解的,所以无论作为Android系统或应用开发者,都有必要好好的学习和了解一下这个过程的。网上有很多介绍应用启动流程源码的文章,但是总感觉大多数都不太全面,很多只是介绍了应用启动过程中的部分流程,而没有总体清晰的认识应用启动过程的总体脉络与系统架构设计思想。所以本文将结合笔者多年来的工作经历,结合systrace分析工具,基于最新Android R AOSP源码完整的分析一下这个从用户手指触控点击屏幕应用图标到应用界面展示到屏幕上的整个应用启动过程,也是对之前所做所学的一个总结与归纳。
2.大纲
- Android触控事件处理机制
- Zygote进程启动和应用进程创建流程
- Handler消息机制
- AMS的Activity组件管理
- 应用Application和Activity组件创建与初始化
- 应用UI布局与绘制
- RenderThread渲染
- SurfaceFlinger合成显示
- 写在最后
- 参考
3. Input触控事件处理流程
3.1 系统机制分析
Android
系统是由事件驱动的,而 input
是最常见的事件之一,用户的点击、滑动、长按等操作,都属于 input
事件驱动,其中的核心就是 InputReader
和 InputDispatcher
。InputReader
和 InputDispatcher
是跑在 SystemServer
进程中的两个 native
循环线程,负责读取和分发 Input
事件。整个处理过程大致流程如下:
InputReader
负责从EventHub
里面把Input
事件读取出来,然后交给InputDispatcher
进行事件分发;InputDispatcher
在拿到InputReader
获取的事件之后,对事件进行包装后,寻找并分发到目标窗口;InboundQueue
队列(“iq”)中放着InputDispatcher
从InputReader
中拿到的input
事件;OutboundQueue
(“oq”)队列里面放的是即将要被派发给各个目标窗口App的事件;WaitQueue
队列里面记录的是已经派发给App
(“wq”),但是App
还在处理没有返回处理成功的事件;PendingInputEventQueue
队列(“aq”)中记录的是应用需要处理的Input
事件,这里可以看到input
事件已经传递到了应用进程;deliverInputEvent
标识App
UI Thread
被Input
事件唤醒;InputResponse
标识Input
事件区域,这里可以看到一个Input_Down
事件 + 若干个Input_Move
事件 + 一个Input_Up
事件的处理阶段都被算到了这里;App
响应处理Input
事件,内部会在其界面View
树中传递处理。
用一张图描述整个过程大致如下:
3.2 结合Systrace分析
从桌面点击应用图标启动应用,system_server
的native
线程InputReader
首先负责从EventHub
中利用linux
的epolle
机制监听并从屏幕驱动读取上报的触控事件,然后唤醒另外一条native线程InputDispatcher
负责进行进一步事件分发。InputDispatcher
中会先将事件放到InboundQueue
也就是“iq”队列中,然后寻找具体处理input
事件的目标应用窗口,并将事件放入对应的目标窗口OutboundQueue
也就是“oq”队列中等待通过SocketPair
双工信道发送到应用目标窗口中。最后当事件发送给具体的应用目标窗口后,会将事件移动到WaitQueue
也就是“wq”中等待目标应用处理事件完成,并开启倒计时,如果目标应用窗口在5S内没有处理完成此次触控事件,就会向system_server
报应用ANR异常事件。以上整个过程在Android
系统源码中都加有相应的systrace tag,如下systrace截图所示:
接着上面的流程继续往下分析:当input
触控事件传递到桌面应用进程后,Input
事件到来后先通过enqueueInputEvent
函数放入“aq”本地待处理队列中,并唤醒应用的UI线程在deliverInputEvent
的流程中进行input
事件的具体分发与处理。具体会先交给在应用界面Window
创建时的ViewRootImpl#setView
流程中创建的多个不同类型的InputStage
中依次进行处理(比如对输入法处理逻辑的封装ImeInputStage
),整个处理流程是按照责任链的设计模式进行。最后会交给ViewPostImeInputStage
中具体进行处理,这里面会从View
布局树的根节点DecorView
开始遍历整个View
树上的每一个子View
或ViewGroup
界面进行事件的分发、拦截、处理的逻辑。最后触控事件处理完成后会调用finishInputEvent
结束应用对触控事件处理逻辑,这里面会通过JNI
调用到native
层InputConsumer
的sendFinishedSignal
函数通知InputDispatcher
事件处理完成,从触发从"wq"队列中及时移除待处理事件以免报ANR
异常。
桌面应用界面View中在连续处理一个ACTION_DOWN
的TouchEvent
触控事件和多个ACTION_MOVE
,直到最后出现一个ACTION_UP
的TouchEvent
事件后,判断属于onClick
点击事件,然后透过ActivityManager
Binder
调用AMS
的startActivity
服务接口触发启动应用的逻辑。从systrace上看如下图所示:
4. 应用进程的创建与启动
4.1 Pause桌面应用
接着上一节继续往下看,桌面进程收到input触控事件并处理后binder
调用框架AMS
的的startActivity
接口启动应用,相关简化代码如下:
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
...
try {
...
// 添加“startActivityInner”的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
// 执行startActivityInner启动应用的逻辑
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
...
}
...
}
在执行startActivityInner
启动应用逻辑中,AMS
中的Activity
栈管理的逻辑,检查发现当前处于前台Resume
状态的Activity
是桌面应用,所以第一步需要通知桌面应用的Activity
进入Paused
状态,相关简化代码逻辑如下:
/*frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java*/
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
...
// mResumedActivity不为null,说明当前存在处于resume状态的Activity且不是新需要启动的应用
if (mResumedActivity != null) {
// 执行startPausingLocked通知桌面应用进入paused状态
pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
}
...
}
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming) {
...
ActivityRecord prev = mResumedActivity;
...
if (prev.attachedToProcess()) {
try {
...
// 相关执行动作封装事务,binder通知mResumedActivity也就是桌面执行pause动作
mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
...
}
}
...
}
桌面应用进程这边执行收到pause
消息后执行Activity
的onPause
生命周期,并在执行完成后,会binder
调用AMS
的activityPaused
接口通知系统执行完activity
的pause
动作,相关代码如下:
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
...
try {
// binder通知AMS当前应用activity已经执行完pause的流程
ActivityTaskManager.getService().activityPaused(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
AMS
这边收到应用的activityPaused
调用后,继续执行启动应用的逻辑,判断需要启动的应用Activity
所在的进程不存在,所以接下来需要先startProcessAsync
创建应用进程,相关简化代码如下:
/*frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java*/
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
final WindowProcessController wpc =
mService.getProcessController(r.processName, r.info.applicationInfo.uid);
...
// 1.如果wpc不为null且hasThread表示应用Activity所属进程存在,直接realStartActivityLocked启动Activity
if (wpc != null && wpc.hasThread()) {
try {
realStartActivityLocked(r, wpc, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
...
}
...
// 2.否则,调用AMS的startProcessAsync正式开始创建应用进程
mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}
以上过程从systrace上看,如下图所示:
- 通知pause桌面应用:
2.确认桌面activityPaused
状态之后,开始创建应用进程:
4.2 创建应用进程
接上一小节的分析可以知道,Android
应用进程的启动是被动式的,在桌面点击图标启动一个应用的组件如Activity
时,如果Activity
所在的进程不存在,就会创建并启动进程。**Android
系统中一般应用进程的创建都是统一由zygote
进程fork
创建的,AMS
在需要创建应用进程时,会通过socket
连接并通知到到zygote
进程在开机阶段就创建好的socket
服务端,然后由zygote
进程fork
创建出应用进程。**整体架构如下图所示:
我们接着上节中的分析,继续从AMS#startProcessAsync
创建进程函数入手,继续看一下应用进程创建相关简化流程代码:
4.2.1 AMS 发送socket请求
/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/
@GuardedBy("this")
final ProcessRecord startProcessLocked(...) {
return mProcessList.startProcessLocked(...);
}
/*frameworks/base/services/core/java/com/android/server/am/ProcessList.java*/
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
int mountExternal, String seInfo, String requiredAbi, String instructionSet,
String invokeWith, long startTime) {
try {
// 原生标识应用进程创建所加的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
...
// 调用Process的start方法创建进程
startResult = Process.start(...);
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
/*frameworks/base/core/java/android/os/Process.java*/
public static ProcessStartResult start(...) {
// 调用ZygoteProcess的start函数
return ZYGOTE_PROCESS.start(...);
}
/*frameworks/base/core/java/android/os/ZygoteProcess.java*/
public final Process.ProcessStartResult start(...){
try {
return startViaZygote(...);
} catch (ZygoteStartFailedEx ex) {
...
}
}
private Process.ProcessStartResult startViaZygote(...){
ArrayList<String> argsForZygote = new ArrayList<String>();
...
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
在ZygoteProcess#startViaZygote
中,最后创建应用进程的逻辑:
openZygoteSocketIfNeeded
函数中打开本地socket
客户端连接到zygote
进程的socket
服务端;zygoteSendArgsAndGetResult
发送socket
请求参数,带上了创建的应用进程参数信息;return
返回的数据结构ProcessStartResult
中会有新创建的进程的pid
字段。
从systrace上看这个过程如下:
4.2.2 Zygote 处理socket请求
其实早在系统开机阶段,zygote
进程创建时,就会在ZygoteInit#main
入口函数中创建服务端socket
,并预加载系统资源和框架类(加速应用进程启动速度),代码如下:
/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
public static void main(String[] argv) {
ZygoteServer zygoteServer = null;
...
try {
...
// 1.preload提前加载框架通用类和系统资源到进程,加速进程启动
preload(bootTimingsTraceLog);
...
// 2.创建zygote进程的socket server服务端对象
zygoteServer = new ZygoteServer(isPrimaryZygote);
...
// 3.进入死循环,等待AMS发请求过来
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
...
} finally {
...
}
...
}
继续往下看ZygoteServer#runSelectLoop
如何监听并处理AMS客户端的请求:
/*frameworks/base/core/java/com/android/internal/os/ZygoteServer.java*/
Runnable runSelectLoop(String abiList) {
// 进入死循环监听
while (true) {
while (--pollIndex >= 0) {
if (pollIndex == 0) {
...
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
// 得到一个请求连接封装对象ZygoteConnection
ZygoteConnection connection = peers.get(pollIndex);
// processCommand函数中处理AMS客户端请求
final Runnable command = connection.processCommand(this, multipleForksOK);
}
}
}
}
Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
...
// 1.fork创建应用子进程
pid = Zygote.forkAndSpecialize(...);
try {
if (pid == 0) {
...
// 2.pid为0,当前处于新创建的子应用进程中,处理请求参数
return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
} else {
...
handleParentProc(pid, serverPipeFd);
}
} finally {
...
}
}
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
...
// 关闭从父进程zygote继承过来的ZygoteServer服务端地址
closeSocket();
...
if (parsedArgs.mInvokeWith != null) {
...
} else {
if (!isZygote) {
// 继续进入ZygoteInit#zygoteInit继续完成子应用进程的相关初始化工作
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, null /* classLoader */);
} else {
...
}
}
}
以上过程从systrace上看如下图所示:
4.2.3 应用进程初始化
接上一节中的分析,zygote
进程监听接收AMS
的请求,fork
创建子应用进程,然后pid
为0时进入子进程空间,然后在 ZygoteInit#zygoteInit
中完成进程的初始化动作,相关简化代码如下:
/*frameworks/base/core/java/com/android/internal/os/ZygoteInit.java*/
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
...
// 原生添加名为“ZygoteInit ”的systrace tag以标识进程初始化流程
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
// 1.RuntimeInit#commonInit中设置应用进程默认的java异常处理机制
RuntimeInit.commonInit();
// 2.ZygoteInit#nativeZygoteInit函数中JNI调用启动进程的binder线程池
ZygoteInit.nativeZygoteInit();
// 3.RuntimeInit#applicationInit中反射创建ActivityThread对象并调用其“main”入口方法
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
应用进程启动后,初始化过程中主要依次完成以下几件事情:
- 应用进程默认的
java
异常处理机制(可以实现监听、拦截应用进程所有的Java crash
的逻辑); JNI
调用启动进程的binder
线程池(注意应用进程的binder
线程池资源是自己创建的并非从zygote
父进程继承的);- 通过反射创建
ActivityThread
对象并调用其“main
”入口方法。
我们继续看RuntimeInit#applicationInit
简化的代码流程:
/*frameworks/base/core/java/com/android/internal/os/RuntimeInit.java*/
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String