Android 应用启动全流程分析(源码深度剖析 + Systrace 展示)

您所在的位置:网站首页 systrace原理 Android 应用启动全流程分析(源码深度剖析 + Systrace 展示)

Android 应用启动全流程分析(源码深度剖析 + Systrace 展示)

2023-05-08 06:48| 来源: 网络整理| 查看: 265

作者:努比亚技术团队转载地址:https://www.jianshu.com/p/37370c1d17fc1.前言

从用户手指点击桌面上的应用图标到屏幕上显示出应用主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接口启动应用,相关简化代码如下:

/*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桌面应用:

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 argsForZygote = new ArrayList(); ... 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系统的运行是受消息机制驱动的,而整个消息机制是由上面所说的四个关键角色相互配合实现的(HandlerLooperMessageQueueMessage),其运行原理如下图所示:

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 等。6. 应用Application和Activity组件创建与初始化6.1 Application的创建与初始化

从前面4.2.3小结中的分析我们知道,应用进程启动初始化执行ActivityThread#main函数过程中,在开启主线程loop消息循环之前,会通过Binder调用系统核心服务AMS的attachApplication接口将自己注册到AMS中。下面我们接着这个流程往下看,我们先从systrace上看看AMS服务的attachApplication接口是如何处理应用进程的attach注册请求的:

我们继续来看相关代码的简化流程:

/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/ @GuardedBy("this") private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { ... if (app.isolatedEntryPoint != null) { ... } else if (instr2 != null) { // 1.通过oneway异步类型的binder调用应用进程ActivityThread#IApplicationThread#bindApplication接口 thread.bindApplication(...); } else { thread.bindApplication(...); } ... // See if the top visible activity is waiting to run in this process... if (normalMode) { try { // 2.继续执行启动应用Activity的流程 didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown launching activities in " + app, e); badApp = true; } } } /*frameworks/base/core/java/android/app/ActivityThread.java*/ private class ApplicationThread extends IApplicationThread.Stub { @Override public final void bindApplication(...) { ... AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; ... // 向应用进程主线程Handler发送BIND_APPLICATION消息,触发在应用主线程执行handleBindApplication初始化动作 sendMessage(H.BIND_APPLICATION, data); } ... } class H extends Handler { ... 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初始化动作 handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; ... } } ... } @UnsupportedAppUsage private void handleBindApplication(AppBindData data) { ... }

从上面的代码流程可以看出:AMS服务在执行应用的attachApplication注册请求过程中,会通过oneway类型的binder调用应用进程ActivityThread#IApplicationThread的bindApplication接口,而bindApplication接口函数实现中又会通过往应用主线程消息队列post BIND_APPLICATION消息触发执行handleBindApplication初始化函数,从systrace看如下图所示:

我们继续结合代码看看handleBindApplication的简化关键流程:

/*frameworks/base/core/java/android/app/ActivityThread.java*/ @UnsupportedAppUsage private void handleBindApplication(AppBindData data) { ... // 1.创建应用的LoadedApk对象 data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); ... // 2.创建应用Application的Context、触发Art虚拟机加载应用APK的Dex文件到内存中,并加载应用APK的Resource资源 final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); ... // 3.调用LoadedApk的makeApplication函数,实现创建应用的Application对象 app = data.info.makeApplication(data.restrictedBackupMode, null); ... // 4.执行应用Application#onCreate生命周期函数 mInstrumentation.onCreate(data.instrumentationArgs); ... }

在ActivityThread#**handleBindApplication初始化过程中在应用主线程中主要完成如下几件事件**:

根据框架传入的ApplicationInfo信息创建应用APK对应的LoadedApk对象;创建应用Application的Context对象;创建类加载器ClassLoader对象并触发Art虚拟机执行OpenDexFilesFromOat动作加载应用APK的Dex文件通过LoadedApk加载应用APK的Resource资源;调用LoadedApk的makeApplication函数,创建应用的Application对象;执行应用Application#onCreate生命周期函数(APP应用开发者能控制的第一行代码);

下面我们结合代码重点看看APK Dex文件的加载和Resource资源的加载流程。

6.1.1 应用APK的Dex文件加载/*frameworks/base/core/java/android/app/ContextImpl.java*/ static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo, String opPackageName) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); // 1.创建应用Application的Context对象 ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null, 0, null, opPackageName); // 2.触发加载APK的DEX文件和Resource资源 context.setResources(packageInfo.getResources()); context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context); return context; } /*frameworks/base/core/java/android/app/LoadedApk.java*/ @UnsupportedAppUsage public Resources getResources() { if (mResources == null) { ... // 加载APK的Resource资源 mResources = ResourcesManager.getInstance().getResources(null, mResDir, splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), getClassLoader()/*触发加载APK的DEX文件*/, null); } return mResources; } @UnsupportedAppUsage public ClassLoader getClassLoader() { synchronized (this) { if (mClassLoader == null) { createOrUpdateClassLoaderLocked(null /*addedPaths*/); } return mClassLoader; } } private void createOrUpdateClassLoaderLocked(List addedPaths) { ... if (mDefaultClassLoader == null) { ... // 创建默认的mDefaultClassLoader对象,触发art虚拟机加载dex文件 mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries( zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, mBaseClassLoader, mApplicationInfo.classLoaderName, sharedLibraries); ... } ... if (mClassLoader == null) { // 赋值给mClassLoader对象 mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader, new ApplicationInfo(mApplicationInfo)); } } /*frameworks/base/core/java/android/app/ApplicationLoaders.java*/ ClassLoader getClassLoaderWithSharedLibraries(...) { // For normal usage the cache key used is the same as the zip path. return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath, libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries); } private ClassLoader getClassLoader(String zip, ...) { ... synchronized (mLoaders) { ... if (parent == baseParent) { ... // 1.创建BootClassLoader加载系统框架类,并增加相应的systrace tag Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); ClassLoader classloader = ClassLoaderFactory.createClassLoader( zip, librarySearchPath, libraryPermittedPath, parent, targetSdkVersion, isBundled, classLoaderName, sharedLibraries); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); ... return classloader; } // 2.创建PathClassLoader加载应用APK的Dex类,并增加相应的systrace tag Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); ClassLoader loader = ClassLoaderFactory.createClassLoader( zip, null, parent, classLoaderName, sharedLibraries); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return loader; } } /*frameworks/base/core/java/com/android/internal/os/ClassLoaderFactory.java*/ public static ClassLoader createClassLoader(...) { // 通过new的方式创建ClassLoader对象,最终会触发art虚拟机加载APK的dex文件 ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null) ? null : sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]); if (isPathClassLoaderName(classloaderName)) { return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries); } ... }

从以上代码可以看出:在创建Application的Context对象后会立马尝试去加载APK的Resource资源,而在这之前需要通过LoadedApk去创建类加载器ClassLoader对象,而这个过程最终就会触发Art虚拟机加载应用APK的dex文件,从systrace上看如下图所示:

具体art虚拟机加载dex文件的流程由于篇幅所限这里就不展开讲了,这边画了一张流程图可以参考一下,感兴趣的读者可以对照追一下源码流程:

6.1.2 应用APK的Resource资源加载/*frameworks/base/core/java/android/app/LoadedApk.java*/ @UnsupportedAppUsage public Resources getResources() { if (mResources == null) { ... // 加载APK的Resource资源 mResources = ResourcesManager.getInstance().getResources(null, mResDir, splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(), getClassLoader()/*触发加载APK的DEX文件*/, null); } return mResources; } /*frameworks/base/core/java/android/app/ResourcesManager.java*/ public @Nullable Resources getResources(...) { try { // 原生Resource资源加载的systrace tag Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources"); ... return createResources(activityToken, key, classLoader, assetsSupplier); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } } private @Nullable Resources createResources(...) { synchronized (this) { ... // 执行创建Resources资源对象 ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier); if (resourcesImpl == null) { return null; } ... } } private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked( @NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) { ... impl = createResourcesImpl(key, apkSupplier); ... } private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) { ... // 创建AssetManager对象,真正实现的APK文件加载解析动作 final AssetManager assets = createAssetManager(key, apkSupplier); ... } private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) { ... for (int i = 0, n = apkKeys.size(); i < n; i++) { final ApkKey apkKey = apkKeys.get(i); try { // 通过loadApkAssets实现应用APK文件的加载 builder.addApkAssets( (apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey)); } catch (IOException e) { ... } } ... } private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException { ... if (key.overlay) { ... } else { // 通过ApkAssets从APK文件所在的路径去加载 apkAssets = ApkAssets.loadFromPath(key.path, key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0); } ... } /*frameworks/base/core/java/android/content/res/ApkAssets.java*/ public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags) throws IOException { return new ApkAssets(FORMAT_APK, path, flags, null /* assets */); } private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { ... // 通过JNI调用Native层的系统system/lib/libandroidfw.so库中的相关C函数实现对APK文件压缩包的解析与加载 mNativePtr = nativeLoad(format, path, flags, assets); ... }

从以上代码可以看出:系统对于应用APK文件资源的加载过程其实就是创建应用进程中的Resources资源对象的过程,其中真正实现APK资源文件的I/O解析作,最终是借助于AssetManager中通过JNI调用系统Native层的相关C函数实现。整个过程从systrace上看如下图所示:

6.2 Activity的创建与初始化

我们回到6.1小结中,看看AMS在收到应用进程的attachApplication注册请求后,先通过oneway类型的binder调用应用及进程的IApplicationThread#bindApplication接口,触发应用进程在主线程执行handleBindeApplication初始化操作,然后继续执行启动应用Activity的操作,下面我们来看看系统是如何启动创建应用Activity的,简化代码流程如下:

/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/ @GuardedBy("this") private boolean attachApplicationLocked(...) { ... if (app.isolatedEntryPoint != null) { ... } else if (instr2 != null) { // 1.通过oneway异步类型的binder调用应用进程ActivityThread#IApplicationThread#bindApplication接口 thread.bindApplication(...); } else { thread.bindApplication(...); } ... // See if the top visible activity is waiting to run in this process... if (normalMode) { try { // 2.继续执行启动应用Activity的流程 didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown launching activities in " + app, e); badApp = true; } } } /*frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java*/ public boolean attachApplication(WindowProcessController wpc) throws RemoteException { synchronized (mGlobalLockWithoutBoost) { if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { // 原生标识attachApplication过程的systrace tag Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName); } try { return mRootWindowContainer.attachApplication(wpc); } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } } /*frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java*/ boolean attachApplication(WindowProcessController app) throws RemoteException { ... final PooledFunction c = PooledLambda.obtainFunction( // startActivityForAttachedApplicationIfNeeded执行启动应用Activity流程 RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this, PooledLambda.__(ActivityRecord.class), app, rootTask.topRunningActivity()); ... } private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r, WindowProcessController app, ActivityRecord top) { ... try { // ActivityStackSupervisor的realStartActivityLocked真正实现启动应用Activity流程 if (mStackSupervisor.realStartActivityLocked(r, app, top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) { ... } } catch (RemoteException e) { .. } } /*frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java*/ boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException { ... // 1.先通过LaunchActivityItem封装Binder通知应用进程执行Launch Activity动作 clientTransaction.addCallback(LaunchActivityItem.obtain(...); // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { // 2.再通过ResumeActivityItem封装Binder通知应用进程执行Launch Resume动作 lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward()); } ... clientTransaction.setLifecycleStateRequest(lifecycleItem); // 执行以上封装的Binder调用 mService.getLifecycleManager().scheduleTransaction(clientTransaction); ... }

从以上代码分析可以看到,框架system_server进程最终是通过ActivityStackSupervisor#realStartActivityLocked函数中,通过LaunchActivityItem和ResumeActivityItem两个类的封装,依次实现binder调用通知应用进程这边执行Activity的Launch和Resume动作的,我们继续往下看相关代码流程:

6.2.1 Activity Create/*frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java*/ @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { // 原生标识Activity Launch的systrace tag Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mIsForward, mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments); // 调用到ActivityThread的handleLaunchActivity函数在主线程执行应用Activity的Launch创建动作 client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } /*frameworks/base/core/java/android/app/ActivityThread.java*/ @Override public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { ... final Activity a = performLaunchActivity(r, customIntent); ... } /** Core implementation of activity launch. */ private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... // 1.创建Activity的Context ContextImpl appContext = createBaseContextForActivity(r); try { //2.反射创建Activity对象 activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... } catch (Exception e) { ... } try { ... if (activity != null) { ... // 3.执行Activity的attach动作 activity.attach(...); ... // 4.执行应用Activity的onCreate生命周期函数,并在setContentView调用中创建DecorView对象 mInstrumentation.callActivityOnCreate(activity, r.state); ... } ... } catch (SuperNotCalledException e) { ... } } /*frameworks/base/core/java/android/app/Activity.java*/ @UnsupportedAppUsage final void attach(...) { ... // 1.创建表示应用窗口的PhoneWindow对象 mWindow = new PhoneWindow(this, window, activityConfigCallback); ... // 2.为PhoneWindow配置WindowManager mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); ... }

从上面代码可以看出,应用进程这边在收到系统binder调用后,在主线程中创建Activiy的流程主要步骤如下

创建Activity的Context;通过反射创建Activity对象;执行Activity的attach动作,其中会创建应用窗口的PhoneWindow对象并设置WindowManage执行应用Activity的onCreate生命周期函数,并在setContentView中创建窗口的DecorView对象

从systrace上看整个过程如下图所示:

6.2.2 Activity Resume/*frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java*/ @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { // 原生标识Activity Resume的systrace tag Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward, "RESUME_ACTIVITY"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } /*frameworks/base/core/java/android/app/ActivityThread.java*/ @Override public void handleResumeActivity(...){ ... // 1.执行performResumeActivity流程,执行应用Activity的onResume生命周期函数 final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); ... if (r.window == null && !a.mFinished && willBeVisible) { ... if (a.mVisibleFromClient) { if (!a.mWindowAdded) { ... // 2.执行WindowManager#addView动作开启视图绘制逻辑 wm.addView(decor, l); } else { ... } } } ... } public ActivityClientRecord performResumeActivity(...) { ... // 执行应用Activity的onResume生命周期函数 r.activity.performResume(r.startsNotResumed, reason); ... } /*frameworks/base/core/java/android/view/WindowManagerGlobal.java*/ public void addView(...) { // 创建ViewRootImpl对象 root = new ViewRootImpl(view.getContext(), display); ... try { // 执行ViewRootImpl的setView函数 root.setView(view, wparams, panelParentView, userId); } catch (RuntimeException e) { ... } }

从上面代码可以看出,应用进程这边在接收到系统Binder调用请求后,在主线程中Activiy Resume的流程主要步骤如下

执行应用Activity的onResume生命周期函数;执行WindowManager的addView动作开启视图绘制逻辑;创建Activity的ViewRootImpl对象;执行ViewRootImpl的setView函数开启UI界面绘制动作

从systrace上看整个过程如下图所示:

7. 应用UI布局与绘制

接上一节的分析,应用主线程中在执行Activity的Resume流程的最后,会创建ViewRootImpl对象并调用其setView函数,从此并开启了应用界面UI布局与绘制的流程。在开始讲解这个过程之前,我们先来整理一下前面代码中讲到的这些概念,如Activity、PhoneWindow、DecorView、ViewRootImpl、WindowManager它们之间的关系与职责,因为这些核心类基本构成了Android系统的GUI显示系统在应用进程侧的核心架构,其整体架构如下图所示:

Window是一个抽象类,通过控制DecorView提供了一些标准的UI方案,比如背景、标题、虚拟按键等,而PhoneWindow是Window的唯一实现类,在Activity创建后的attach流程中创建,应用启动显示的内容装载到其内部的mDecor(DecorView);DecorView是整个界面布局View控件树的根节点,通过它可以遍历访问到整个View控件树上的任意节点;WindowManager是一个接口,继承自ViewManager接口,提供了View的基本操作方法;WindowManagerImp实现了WindowManager接口,内部通过组合方式持有WindowManagerGlobal,用来操作View;WindowManagerGlobal是一个全局单例,内部可以通过ViewRootImpl将View添加至窗口中ViewRootImpl是所有View的Parent,用来总体管理View的绘制以及与系统WMS窗口管理服务的IPC交互从而实现窗口的开辟;ViewRootImpl是应用进程运转的发动机,可以看到ViewRootImpl内部包含mView(就是DecorView)、mSurface、Choregrapher,mView代表整个控件树,mSurfacce代表画布,应用的UI渲染会直接放到mSurface中,Choregorapher使得应用请求vsync信号,接收信号后开始渲染流程;我们从ViewRootImpl的setView流程继续结合代码往下看:/*frameworks/base/core/java/android/view/ViewRootImpl.java*/ public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { synchronized (this) { if (mView == null) { mView = view; } ... // 开启绘制硬件加速,初始化RenderThread渲染线程运行环境 enableHardwareAcceleration(attrs); ... // 1.触发绘制动作 requestLayout(); ... inputChannel = new InputChannel(); ... // 2.Binder调用访问系统窗口管理服务WMS接口,实现addWindow添加注册应用窗口的操作,并传入inputChannel用于接收触控事件 res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mDisplayCutout, inputChannel, mTempInsets, mTempControls); ... // 3.创建WindowInputEventReceiver对象,实现应用窗口接收触控事件 mInputEventReceiver = new WindowInputEventReceiver(inputChannel, Looper.myLooper()); ... // 4.设置DecorView的mParent为ViewRootImpl view.assignParent(this); ... } }

从以上代码可以看出ViewRootImpl的setView内部关键流程如下:

requestLayout()通过一系列调用触发界面绘制(measure、layout、draw)动作,下文会详细展开分析;通过Binder调用访问系统窗口管理服务WMS的addWindow接口实现添加、注册应用窗口的操作,并传入本地创建inputChannel对象用于后续接收系统的触控事件,这一步执行完我们的View就可以显示到屏幕上了。关于WMS的内部实现流程也非常复杂,由于篇幅有限本文就不详细展开分析了。创建WindowInputEventReceiver对象,封装实现应用窗口接收系统触控事件的逻辑;执行view.assignParent(this),设置DecorView的mParent为ViewRootImpl。所以,虽然ViewRootImpl不是一个View,但它是所有View的顶层Parent。

我们顺着ViewRootImpl的requestLayout动作继续往下看界面绘制的流程代码:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/ public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { // 检查当前UI绘制操作是否发生在主线程,如果发生在子线程则会抛出异常 checkThread(); mLayoutRequested = true; // 触发绘制操作 scheduleTraversals(); } } @UnsupportedAppUsage void scheduleTraversals() { if (!mTraversalScheduled) { ... // 注意此处会往主线程的MessageQueue消息队列中添加同步栏删,因为系统绘制消息属于异步消息,需要更高优先级的处理 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); // 通过Choreographer往主线程消息队列添加CALLBACK_TRAVERSAL绘制类型的待执行消息,用于触发后续UI线程真正实现绘制动作 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } }

Choreographer 的引入,主要是配合系统Vsync垂直同步机制(Android“黄油计划”中引入的机制之一,协调APP生成UI数据和SurfaceFlinger合成图像,避免Tearing画面撕裂的现象),给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统通过对 Vsync 信号周期的调整,来控制每一帧绘制操作的时机。Choreographer 扮演 Android 渲染链路中承上启下的角色

承上:负责接收和处理 App 的各种更新消息和回调,等到 Vsync 到来的时候统一处理。比如集中处理 Input(主要是 Input 事件的处理) 、Animation(动画相关)、Traversal(包括 measure、layout、draw 等操作) ,判断卡顿掉帧情况,记录 CallBack 耗时等;启下:负责请求和接收 Vsync 信号。接收 Vsync 事件回调(通过 FrameDisplayEventReceiver.onVsync ),请求 Vsync(FrameDisplayEventReceiver.scheduleVsync) 。

Choreographer在收到CALLBACK_TRAVERSAL类型的绘制任务后,其内部的工作流程如下图所示:

从以上流程图可以看出:ViewRootImpl调用Choreographer的postCallback接口放入待执行的绘制消息后,Choreographer会先向系统申请APP 类型的vsync信号,然后等待系统vsync信号到来后,去回调到ViewRootImpl的doTraversal函数中执行真正的绘制动作(measure、layout、draw)。这个绘制过程从systrace上看如下图所示:

我们接着ViewRootImpl的doTraversal函数的简化代码流程往下看:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/ void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; // 调用removeSyncBarrier及时移除主线程MessageQueue中的Barrier同步栏删,以避免主线程发生“假死” mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); ... // 执行具体的绘制任务 performTraversals(); ... } } private void performTraversals() { ... // 1.从DecorView根节点出发,遍历整个View控件树,完成整个View控件树的measure测量操作 windowSizeMayChange |= measureHierarchy(...); ... if (mFirst...) { // 2.第一次执行traversals绘制任务时,Binder调用访问系统窗口管理服务WMS的relayoutWindow接口,实现WMS计算应用窗口尺寸并向系统surfaceflinger正式申请Surface“画布”操作 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); } ... // 3.从DecorView根节点出发,遍历整个View控件树,完成整个View控件树的layout测量操作 performLayout(lp, mWidth, mHeight); ... // 4.从DecorView根节点出发,遍历整个View控件树,完成整个View控件树的draw测量操作 performDraw(); ... } private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { ... // 通过Binder IPC访问系统WMS服务的relayout接口,申请Surface“画布”操作 int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mSurfaceSize, mBlastSurfaceControl); if (mSurfaceControl.isValid()) { if (!useBLAST()) { // 本地Surface对象获取指向远端分配的Surface的引用 mSurface.copyFrom(mSurfaceControl); } else { ... } } ... } private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { ... // 原生标识View树的measure测量过程的trace tag Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { // 从mView指向的View控件树的根节点DecorView出发,遍历访问整个View树,并完成整个布局View树的测量工作 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } private void performDraw() { ... boolean canUseAsync = draw(fullRedrawNeeded); ... } private boolean draw(boolean fullRedrawNeeded) { ... if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { ... // 如果开启并支持硬件绘制加速,则走硬件绘制的流程(从Android 4.+开始,默认情况下都是支持跟开启了硬件加速的) mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); } else { // 否则走drawSoftware软件绘制的流程 if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty, surfaceInsets)) { return false; } } }

从上面的代码流程可以看出,ViewRootImpl中负责的整个应用界面绘制的主要流程如下

从界面View控件树的根节点DecorView出发,递归遍历整个View控件树,完成对整个View控件树的measure测量操作,由于篇幅所限,本文就不展开分析这块的详细流程;界面第一次执行绘制任务时,会通过Binder IPC访问系统窗口管理服务WMS的relayout接口,实现窗口尺寸的计算并向系统申请用于本地绘制渲染的Surface“画布”的操作(具体由SurfaceFlinger负责创建应用界面对应的BufferQueueLayer对象,并通过内存共享的方式通过Binder将地址引用透过WMS回传给应用进程这边),由于篇幅所限,本文就不展开分析这块的详细流程;从界面View控件树的根节点DecorView出发,递归遍历整个View控件树,完成对整个View控件树的layout测量操作;从界面View控件树的根节点DecorView出发,递归遍历整个View控件树,完成对整个View控件树的draw测量操作,如果开启并支持硬件绘制加速(从Android 4.X开始谷歌已经默认开启硬件加速),则走GPU硬件绘制的流程,否则走CPU软件绘制的流程

以上绘制过程从systrace上看如下图所示:

借用一张图来总结应用UI绘制的流程,如下所示:

8. RenderThread渲染

截止到目前,在ViewRootImpl中完成了对界面的measure、layout和draw等绘制流程后,用户依然还是看不到屏幕上显示的应用界面内容,因为整个Android系统的显示流程除了前面讲到的UI线程的绘制外,界面还需要经过RenderThread线程的渲染处理,渲染完成后,还需要通过Binder调用“上帧”交给surfaceflinger进程中进行合成后送显才能最终显示到屏幕上。本小节中,我们将接上一节中ViewRootImpl中最后draw的流程继续往下分析开启硬件加速情况下,RenderThread渲染线程的工作流程。由于目前Android 4.X之后系统默认界面是开启硬件加速的,所以本文我们重点分析硬件加速条件下的界面渲染流程,我们先分析一下简化的代码流程:

/*frameworks/base/core/java/android/view/ViewRootImpl.java*/ private boolean draw(boolean fullRedrawNeeded) { ... if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { ... // 硬件加速条件下的界面渲染流程 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); } else { ... } } /*frameworks/base/core/java/android/view/ThreadedRenderer.java*/ void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) { ... // 1.从DecorView根节点出发,递归遍历View控件树,记录每个View节点的绘制操作命令,完成绘制操作命令树的构建 updateRootDisplayList(view, callbacks); ... // 2.JNI调用同步Java层构建的绘制命令树到Native层的RenderThread渲染线程,并唤醒渲染线程利用OpenGL执行渲染任务; int syncResult = syncAndDrawFrame(choreographer.mFrameInfo); ... }

从上面的代码可以看出,硬件加速绘制主要包括两个阶段

从DecorView根节点出发,递归遍历View控件树,记录每个View节点的drawOp绘制操作命令,完成绘制操作命令树的构建;JNI调用同步Java层构建的绘制命令树到Native层的RenderThread渲染线程,并唤醒渲染线程利用OpenGL执行渲染任务;8.1 构建绘制命令树

我们先来看看第一阶段构建绘制命令树的代码简化流程:

/*frameworks/base/core/java/android/view/ThreadedRenderer.java*/ private void updateRootDisplayList(View view, DrawCallbacks callbacks) { // 原生标记构建View绘制操作命令树过程的systrace tag Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()"); // 递归子View的updateDisplayListIfDirty实现构建DisplayListOp updateViewTreeDisplayList(view); ... if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) { // 获取根View的SkiaRecordingCanvas RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight); try { ... // 利用canvas缓存DisplayListOp绘制命令 canvas.drawRenderNode(view.updateDisplayListIfDirty()); ... } finally { // 将所有DisplayListOp绘制命令填充到RootRenderNode中 mRootNode.endRecording(); } } Trace.traceEnd(Trace.TRACE_TAG_VIEW); } private void updateViewTreeDisplayList(View view) { ... // 从DecorView根节点出发,开始递归调用每个View树节点的updateDisplayListIfDirty函数 view.updateDisplayListIfDirty(); ... } /*frameworks/base/core/java/android/view/View.java*/ public RenderNode updateDisplayListIfDirty() { ... // 1.利用`View`对象构造时创建的`RenderNode`获取一个`SkiaRecordingCanvas`“画布”; final RecordingCanvas canvas = renderNode.beginRecording(width, height); try { ... if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { // 如果仅仅是ViewGroup,并且自身不用绘制,直接递归子View dispatchDraw(canvas); ... } else { // 2.利用SkiaRecordingCanvas,在每个子View控件的onDraw绘制函数中调用drawLine、drawRect等绘制操作时,创建对应的DisplayListOp绘制命令,并缓存记录到其内部的SkiaDisplayList持有的DisplayListData中; draw(canvas); } } finally { // 3.将包含有`DisplayListOp`绘制命令缓存的`SkiaDisplayList`对象设置填充到`RenderNode`中; renderNode.endRecording(); ... } ... } public void draw(Canvas canvas) { ... // draw the content(View自己实现的onDraw绘制,由应用开发者自己实现) onDraw(canvas); ... // draw the children dispatchDraw(canvas); ... } /*frameworks/base/graphics/java/android/graphics/RenderNode.java*/ public void endRecording() { ... // 从SkiaRecordingCanvas中获取SkiaDisplayList对象 long displayList = canvas.finishRecording(); // 将SkiaDisplayList对象填充到RenderNode中 nSetDisplayList(mNativeRenderNode, displayList); canvas.recycle(); }

从以上代码可以看出,构建绘制命令树的过程是从View控件树的根节点DecorView触发,递归调用每个子View节点的updateDisplayListIfDirty函数,最终完成绘制树的创建,简述流程如下

利用View对象构造时创建的RenderNode获取一个SkiaRecordingCanvas“画布”;利用SkiaRecordingCanvas,在每个子View控件的onDraw绘制函数中调用drawLine、drawRect等绘制操作时,创建对应的DisplayListOp绘制命令,并缓存记录到其内部的SkiaDisplayList持有的DisplayListData中;将包含有DisplayListOp绘制命令缓存的SkiaDisplayList对象设置填充到RenderNode中;最后将根View的缓存DisplayListOp设置到RootRenderNode中,完成构建。

以上整个构建绘制命令树的过程可以用如下流程图表示:

硬件加速下的整个界面的View树的结构如下图所示:

最后从systrace上看这个过程如下图所示:

8.2 执行渲染绘制任务

经过上一小节中的分析,应用在UI线程中从根节点DecorView出发,递归遍历每个子View节点,搜集其drawXXX绘制动作并转换成DisplayListOp命令,将其记录到DisplayListData并填充到RenderNode中,最终完成整个View绘制命令树的构建。从此UI线程的绘制任务就完成了。下一步UI线程将唤醒RenderThread渲染线程,触发其利用OpenGL执行界面的渲染任务,本小节中我们将重点分析这个流程。我们还是先看看这块代码的简化流程:

/*frameworks/base/graphics/java/android/graphics/HardwareRenderer.java*/ public int syncAndDrawFrame(@NonNull FrameInfo frameInfo) { // JNI调用native层的相关函数 return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length); } /*frameworks/base/libs/hwui/jni/android_graphics_HardwareRenderer.cpp*/ static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { ... RenderProxy* proxy = reinterpret_cast(proxyPtr); env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); return proxy->syncAndDrawFrame(); } /*frameworks/base/libs/hwui/renderthread/RenderProxy.cpp*/ int RenderProxy::syncAndDrawFrame() { // 唤醒RenderThread渲染线程,执行DrawFrame绘制任务 return mDrawFrameTask.drawFrame(); } /*frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp*/ int DrawFrameTask::drawFrame() { ... postAndWait(); ... } void DrawFrameTask::postAndWait() { AutoMutex _lock(mLock); // 向RenderThread渲染线程的MessageQueue消息队列放入一个待执行任务,以将其唤醒执行run函数 mRenderThread->queue().post([this]() { run(); }); // UI线程暂时进入wait等待状态 mSignal.wait(mLock); } void DrawFrameTask::run() { // 原生标识一帧渲染绘制任务的systrace tag ATRACE_NAME("DrawFrame"); ... { TreeInfo info(TreeInfo::MODE_FULL, *mContext); //1.将UI线程构建的DisplayListOp绘制命令树同步到RenderThread渲染线程 canUnblockUiThread = syncFrameState(info); ... } ... // 同步完成后则可以唤醒UI线程 if (canUnblockUiThread) { unblockUiThread(); } ... if (CC_LIKELY(canDrawThisFrame)) { // 2.执行draw渲染绘制动作 context->draw(); } else { ... } ... } bool DrawFrameTask::syncFrameState(TreeInfo& info) { ATRACE_CALL(); ... // 调用CanvasContext的prepareTree函数实现绘制命令树同步的流程 mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode); ... } /*frameworks/base/libs/hwui/renderthread/CanvasContext.cpp*/ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target) { ... for (const sp& node : mRenderNodes) { ... // 递归调用各个子View对应的RenderNode执行prepareTree动作 node->prepareTree(info); ... } ... } /*frameworks/base/libs/hwui/RenderNode.cpp*/ void RenderNode::prepareTree(TreeInfo& info) { ATRACE_CALL(); ... prepareTreeImpl(observer, info, false); ... } void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { ... if (info.mode == TreeInfo::MODE_FULL) { // 同步绘制命令树 pushStagingDisplayListChanges(observer, info); } if (mDisplayList) { // 遍历调用各个子View对应的RenderNode的prepareTreeImpl bool isDirty = mDisplayList->prepareListAndChildren( observer, info, childFunctorsNeedLayer, [](RenderNode* child, TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { child->prepareTreeImpl(observer, info, functorsNeedLayer); }); ... } ... } void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) { ... syncDisplayList(observer, &info); ... } void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { ... // 完成赋值同步DisplayList对象 mDisplayList = mStagingDisplayList; mStagingDisplayList = nullptr; ... } void CanvasContext::draw() { ... // 1.调用OpenGL库使用GPU,按照构建好的绘制命令完成界面的渲染 bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); ... // 2.将前面已经绘制渲染好的图形缓冲区Binder上帧给SurfaceFlinger合成和显示 bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); ... }

从以上代码可以看出:UI线程利用RenderProxy向RenderThread线程发送一个DrawFrameTask任务请求,RenderThread被唤醒,开始渲染,大致流程如下

syncFrameState中遍历View树上每一个RenderNode,执行prepareTreeImpl函数,实现同步绘制命令树的操作;调用OpenGL库API使用GPU,按照构建好的绘制命令完成界面的渲染(具体过程,由于本文篇幅所限,暂不展开分析);将前面已经绘制渲染好的图形缓冲区Binder上帧给SurfaceFlinger合成和显示;

整个过程可以用如下流程图表示:

从systrace上这个过程如下图所示:

9. SurfaceFlinger合成显示

SurfaceFlinger合成显示部分完全属于Android系统GUI中图形显示的内容,逻辑结构也比较复杂,但不属于本文介绍内容的重点。所以本小节中只是总体上介绍一下其工作原理与思想,不再详细分析源码,感兴趣的读者可以关注笔者后续的文章再来详细分析讲解。简单的说SurfaceFlinger作为系统中独立运行的一个Native进程,借用Android官网的描述,其职责就是负责接受来自多个来源的数据缓冲区,对它们进行合成,然后发送到显示设备。如下图所示:

从上图可以看出,其实SurfaceFlinger在Android系统的整个图形显示系统中是起到一个承上启下的作用

对上:通过Surface与不同的应用进程建立联系,接收它们写入Surface中的绘制缓冲数据,对它们进行统一合成。对下:通过屏幕的后缓存区与屏幕建立联系,发送合成好的数据到屏幕显示设备。

图形的传递是通过Buffer作为载体,Surface是对Buffer的进一步封装,也就是说Surface内部具有多个Buffer供上层使用,如何管理这些Buffer呢?答案就是BufferQueue ,下面我们来看看BufferQueue的工作原理:

9.1 BufferQueue机制

借用一张经典的图来描述BufferQueue的工作原理:

BufferQueue是一个典型的生产者-消费者模型中的数据结构。在Android应用的渲染流程中,应用扮演的就是“生产者”的角色,而SurfaceFlinger扮演的则是“消费者”的角色,其配合工作的流程如下

应用进程中在开始界面的绘制渲染之前,需要通过Binder调用dequeueBuffer接口从SurfaceFlinger进程中管理的BufferQueue 中申请一张处于free状态的可用Buffer,如果此时没有可用Buffer则阻塞等待;应用进程中拿到这张可用的Buffer之后,选择使用CPU软件绘制渲染或GPU硬件加速绘制渲染,渲染完成后再通过Binder调用queueBuffer接口将缓存数据返回给应用进程对应的BufferQueue(如果是 GPU 渲染的话,这里还有个 GPU处理的过程,所以这个 Buffer 不会马上可用,需要等 GPU 渲染完成的Fence信号),并申请sf类型的Vsync以便唤醒“消费者”SurfaceFlinger进行消费;SurfaceFlinger 在收到 Vsync 信号之后,开始准备合成,使用 acquireBuffer获取应用对应的 BufferQueue 中的 Buffer 并进行合成操作;合成结束后,SurfaceFlinger 将通过调用 releaseBuffer将 Buffer 置为可用的free状态,返回到应用对应的 BufferQueue中。9.2 Vsync同步机制

Vysnc垂直同步是Android在“黄油计划”中引入的一个重要机制,本质上是为了协调BufferQueue的应用生产者生成UI数据动作和SurfaceFlinger消费者的合成消费动作,避免出现画面撕裂的Tearing现象。Vysnc信号分为两种类型:

app类型的Vsync:app类型的Vysnc信号由上层应用中的Choreographer根据绘制需求进行注册和接收,用于控制应用UI绘制上帧的生产节奏。根据第7小结中的分析:应用在UI线程中调用invalidate刷新界面绘制时,需要先透过Choreographer向系统申请注册app类型的Vsync信号,待Vsync信号到来后,才能往主线程的消息队列放入待绘制任务进行真正UI的绘制动作;sf类型的Vsync:sf类型的Vsync是用于控制SurfaceFlinger的合成消费节奏。应用完成界面的绘制渲染后,通过Binder调用queueBuffer接口将缓存数据返还给应用对应的BufferQueue时,会申请sf类型的Vsync,待SurfaceFlinger 在其UI线程中收到 Vsync 信号之后,便开始进行界面的合成操作。

Vsync信号的生成是参考屏幕硬件的刷新周期的,其架构如下图所示:

本小节所描述的流程,从systrace上看SurfaceFlinger处理应用上帧工作的流程如下图所示:

10.写在最后

至此,本文结合源码和systrace完整的分析了从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面第一帧画面的完整流程,这其中涉及了App应用、system_server框架、Art虚拟机、surfaceflinger等一系列Android系统核心模块的相互配合,有很多的细节也由于篇幅所限无法完全展开分析,感兴趣的读者可以结合AOSP源码继续深入分析。而优化应用启动打开的速度这个系统核心用户体验的指标,也是多少年来谷歌、SOC芯片厂商、ODM手机厂商以及各个应用开发者共同努力优化的方向:

对于SOC芯片厂商而言:需要不断升级CPU和GPU的硬件算力;对于Android系统的维护者谷歌而言:在Android系统大版本升级过程中,不断的优化应用启动过程上的各个系统流程,比如进程创建的速度优化、Art虚拟机的引入与性能优化、View绘制流程的简化、硬件绘制加速机制的引入、系统核心AMS、WMS等核心服务的锁优化等;对于各个ODM手机厂商而言:会开发识别应用启动的场景,进行针对性的CPU主频的拉升调节、触控响应速度的优化等机制;对于各个应用开发者而言:会结合自己的业务对应用启动的场景进行优化,比如尽量减少或推迟在Application、Activity生命周期函数中的初始化逻辑、去除界面布局的过度绘制、异步化的布局XML文件解析等机制。

本文只是分析了应用启动一般性流程,至于如何去优化应用启动的速度,可以关注笔者后续文章的更新,而本文则可以作为应用启动优化的一个基础认知。最后用一张流程图来概述一下应用启动流程的全貌:

该图太大可能导致图片清晰度下降,如需要完整版的可以去我 Github 进行访问查阅。GitHub地址: https://github.com/733gh/Android-T3

针对上面的思维导图里面的知识点,我还对应的整理相关的学习文档,分享给大家进行参考学习。

▲ Android 性能优化核心笔记 ▲如需要参考完整版请点击下方小卡片或去我GitHub进行访问查阅。GitHub地址: https://github.com/733gh/Android-T3



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3