本文是努比亚技术团队原创内容,转载已经获得作者同意,原文地址:www.jianshu.com/p/37370c1d1…
加入努比亚
我们的团队主要负责 Nubia 红魔电竞手机系统整体性能优化,我们专注于性能、显示、框架、Kernel 等方向的深耕,我们的愿景是打造红魔电竞游戏手机的极致性能体验,创造业界最优秀的游戏手机
如果你对技术充满热情与极致追求,欢迎加入努比亚技术团队。Nubia 技术团队持续招聘 Android 应用 / 系统 / 驱动 / 通信 / 协议及性能优化相关开发岗位,校招点击(hr.nubia.com)投递简历,社招内推渠道:发送简历到 johnny.xiao@nubia.com,邮件标题:姓名 - 工作年限 - 应聘方向
本文分上下两篇,欢迎大家点赞收藏,配合自己抓一个应用冷启动的 Systrace 来看印象会更深
高爷:Android 应用启动全流程分析(源码深度剖析和 Systrace 展示)- 上篇
高爷:Android 应用启动全流程分析(源码深度剖析和 Systrace 展示)- 下篇Android Systrace 基础知识 -- Systrace 简介
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 流程中创建的多个不同类型的 InputUsage 中依次进行处理(比如对输入法处理逻辑的封装 ImeInputUsage),整个处理流程是按照责任链的设计模式进行。最后会交给 ViewpostImeInputUsage 中具体进行处理,这里面会从 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 接口启动应用,相关简化代码如下:
/*frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java*/
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 动作,相关代码如下:
/*frameworks/base/core/java/android/app/servertransaction/PauseActivityItem.java*/
@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 桌面应用:
- 确认桌面 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[] argv, ClassLoader classLoader) {
...
// 结束“ZygoteInit ”的systrace tag
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
// 1.反射加载创建ActivityThread类对象
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
...
}
Method m;
try {
// 2.反射调用其main方法
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
...
} catch (SecurityException ex) {
...
}
...
// 3.触发执行以上逻辑
return new MethodAndArgsCaller(m, argv);
}
我们继续往下看 ActivityThread 的 main 函数中又干了什么:
/*frameworks/base/core/java/android/app/ActivityThread.java*/
public static void main(String[] args) {
// 原生添加的标识进程ActivityThread初始化过程的systrace tag,名为“ActivityThreadMain”
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
...
// 1.创建并启动主线程的loop消息循环
Looper.prepareMainLooper();
...
// 2.attachApplication注册到系统AMS中
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
...
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
...
}
private void attach(boolean system, long startSeq) {
...
if (!system) {
...
final IActivityManager mgr = ActivityManager.getService();
try {
// 通过binder调用AMS的attachApplication接口将自己注册到AMS中
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
可以看到进程 ActivityThread#main 函数初始化的主要逻辑是:
- 创建并启动主线程的 loop 消息循环;
- 通过 binder 调用 AMS 的 attachApplication 接口将自己 attach 注册到 AMS 中。
以上初始化过程。从 systrace 上看如下图所示:
5. 应用主线程消息循环机制建立
接上一节的分析,我们知道应用进程创建后会通过反射创建 ActivityThread 对象并执行其 main 函数,进行主线程的初始化工作:
/*frameworks/base/core/java/android/app/ActivityThread.java*/
public static void main(String[] args) {
...
// 1.创建Looper、MessageQueue
Looper.prepareMainLooper();
...
// 2.启动loop消息循环,开始准备接收消息
Looper.loop();
...
}
// 3.创建主线程Handler对象
final H mH = new H();
class H extends Handler {
...
}
/*frameworks/base/core/java/android/os/Looper.java*/
public static void prepareMainLooper() {
// 准备主线程的Looper
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建主线程的Looper对象,并通过ThreadLocal机制实现与主线程的一对一绑定
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
// 创建MessageQueue消息队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
主线程初始化完成后,主线程就有了完整的 Looper、MessageQueue、Handler,此时 ActivityThread 的 Handler 就可以开始处理 Message,包括 Application、Activity、ContentProvider、Service、Broadcast 等组件的生命周期函数,都会以 Message 的形式,在主线程按照顺序处理,这就是 App 主线程的初始化和运行原理,部分处理的 Message 如下
/*frameworks/base/core/java/android/app/ActivityThread.java*/
class H extends Handler {
public static final int BIND_APPLICATION = 110;
@UnsupportedAppUsage
public static final int RECEIVER = 113;
@UnsupportedAppUsage
public static final int CREATE_SERVICE = 114;
@UnsupportedAppUsage
public static final int BIND_SERVICE = 121;
public void handleMessage(Message msg) {
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
}
...
}
主线程初始化完成后,主线程就进入阻塞状态,等待 Message,一旦有 Message 发过来,主线程就会被唤醒,处理 Message,处理完成之后,如果没有其他的 Message 需要处理,那么主线程就会进入休眠阻塞状态继续等待。可以说 Android 系统的运行是受消息机制驱动的,而整个消息机制是由上面所说的四个关键角色相互配合实现的(Handler、Looper、MessageQueue、Message),其运行原理如下图所示:
- Handler : Handler 主要是用来处理 Message,应用可以在任何线程创建 Handler,只要在创建的时候指定对应的 Looper 即可,如果不指定,默认是在当前 Thread 对应的 Looper。
- Looper : Looper 可以看成是一个循环器,其 loop 方法开启后,不断地从 MessageQueue 中获取 Message,对 Message 进行 Delivery 和 Dispatch,最终发给对应的 Handler 去处理。
- MessageQueue:MessageQueue 就是一个 Message 管理器,队列中是 Message,在没有 Message 的时候,MessageQueue 借助 Linux 的 ePoll 机制,阻塞休眠等待,直到有 Message 进入队列将其唤醒。
- Message:Message 是传递消息的对象,其内部包含了要传递的内容,最常用的包括 what、arg、callback 等。