当前位置: 首页 > news >正文

网站分析对比产品展示型网站赏析

网站分析对比,产品展示型网站赏析,个人博客主页登录,wordpress同步twitter作者#xff1a;努比亚技术团队 源码来源#xff1a;努比亚技术团队 1.前言 从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面而完成应用启动#xff0c;快的话往往都不需要一秒钟#xff0c;但是这整个过程却是十分复杂的#xff0c;其中涉及了Android系… 作者努比亚技术团队 源码来源努比亚技术团队 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 tagTrace.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动作相关代码如下 Overridepublic 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启动Activityif (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 tagTrace.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(...){ArrayListString argsForZygote new ArrayListString();...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// 得到一个请求连接封装对象ZygoteConnectionZygoteConnection 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 tagTrace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);// Remaining arguments are passed to the start classs static mainreturn 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、MessageQueueLooper.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() {// 准备主线程的Looperprepare(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;UnsupportedAppUsagepublic static final int RECEIVER 113;UnsupportedAppUsagepublic static final int CREATE_SERVICE 114;UnsupportedAppUsagepublic 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 等。 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 {Overridepublic 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(ListString 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 tagTrace.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 tagTrace.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 tagTrace.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 tagTrace.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 tagTrace.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的ContextContextImpl 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*/UnsupportedAppUsagefinal void attach(...) {...// 1.创建表示应用窗口的PhoneWindow对象mWindow new PhoneWindow(this, window, activityConfigCallback);...// 2.为PhoneWindow配置WindowManagermWindow.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 tagTrace.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流程中创建应用启动显示的内容装载到其内部的mDecorDecorViewDecorView是整个界面布局View控件树的根节点通过它可以遍历访问到整个View控件树上的任意节点WindowManager是一个接口继承自ViewManager接口提供了View的基本操作方法WindowManagerImp实现了WindowManager接口内部通过组合方式持有WindowManagerGlobal用来操作ViewWindowManagerGlobal是一个全局单例内部可以通过ViewRootImpl将View添加至窗口中ViewRootImpl是所有View的Parent用来总体管理View的绘制以及与系统WMS窗口管理服务的IPC交互从而实现窗口的开辟ViewRootImpl是应用进程运转的发动机可以看到ViewRootImpl内部包含mView就是DecorView、mSurface、ChoregraphermView代表整个控件树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为ViewRootImplview.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 tagTrace.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 tagTrace.traceBegin(Trace.TRACE_TAG_VIEW, Record View#draw());// 递归子View的updateDisplayListIfDirty实现构建DisplayListOpupdateViewTreeDisplayList(view);...if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {// 获取根View的SkiaRecordingCanvasRecordingCanvas 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并且自身不用绘制直接递归子ViewdispatchDraw(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 childrendispatchDraw(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_castRenderProxy*(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 tagATRACE_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 spRenderNode 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的prepareTreeImplbool 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类型的Vsyncapp类型的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文件解析等机制。 本文只是分析了应用启动一般性流程至于如何去优化应用启动的速度可以关注笔者后续文章的更新而本文则可以作为应用启动优化课题的一个基础认知。最后用一张流程图来概述一下应用启动流程的全貌 如果你还没有掌握Framework现在想要在最短的时间里吃透它可以参考一下《Android Framework核心知识点》里面内容包含了Init、Zygote、SystemServer、Binder、Handler、AMS、PMS、Launcher……等知识点记录。 《Framework 核心知识点汇总手册》:https://qr18.cn/AQpN4J Handler 机制实现原理部分 1.宏观理论分析与Message源码分析 2.MessageQueue的源码分析 3.Looper的源码分析 4.handler的源码分析 5.总结 Binder 原理 1.学习Binder前必须要了解的知识点 2.ServiceManager中的Binder机制 3.系统服务的注册过程 4.ServiceManager的启动过程 5.系统服务的获取过程 6.Java Binder的初始化 7.Java Binder中系统服务的注册过程 Zygote Android系统的启动过程及Zygote的启动过程应用进程的启动过程 AMS源码分析  Activity生命周期管理onActivityResult执行过程AMS中Activity栈管理详解 深入PMS源码 1.PMS的启动过程和执行流程 2.APK的安装和卸载源码分析 3.PMS中intent-filter的匹配架构 WMS 1.WMS的诞生 2.WMS的重要成员和Window的添加过程 3.Window的删除过程 《Android Framework学习手册》https://qr18.cn/AQpN4J 开机Init 进程开机启动 Zygote 进程开机启动 SystemServer 进程Binder 驱动AMS 的启动过程PMS 的启动过程Launcher 的启动过程Android 四大组件Android 系统服务 - Input 事件的分发过程Android 底层渲染 - 屏幕刷新机制源码分析Android 源码分析实战
http://www.pierceye.com/news/216372/

相关文章:

  • 网站建设,h5,小程序众安保险
  • 大连网站建设资讯网站seo如何优化
  • 手表网站建设策划西地那非片怎么服用最佳
  • 常德网站设计英文版网站怎么做
  • 权威网站建设网站的工具
  • php手机网站模板厦门网站设计建设
  • 焦作集团网站建设做食品网站需要什么资质
  • 西北电力建设甘肃工程公司网站90设计电商模板
  • 内蒙古网站设计推广网站注册赚佣金
  • 医药类网站建设评价wordpress微信支付模板
  • 如何查看网站空间商手机服务器下载安装
  • 北京响应式网站建设报价英文版网站案例
  • 做爰全过程免费的视频99网站做h5单页的网站
  • 怎么才能百度做网站海外直播
  • 响应式企业网站开发所用的平台酷炫网站首页
  • 西安网站建设全包大发 wordpress
  • html5 网站开发定制做公司网站好处
  • 建站网站教程网站建设工程师职责说明书
  • 新云网站模版宠物网站开发
  • 网站建设面授班网站备案回访电话号码
  • 阿里有做网站网站建设费是宣传费用吗
  • 点广告挣钱网站有哪些网站的建设与预算
  • 佛山新网站建设详细内容手机软件公司
  • 网站建设美词原创怎样建设一个能上传数据的网站
  • 网站建设网站营销做APP必须要有网站么
  • 易企建站咖啡公司网站建设策划书
  • wordpress 不能查看站点网站建设公司软件开
  • 网站文章seoftp搭建wordpress
  • 济宁企业做网站受欢迎的常州做网站
  • 我有域名有服务器怎么建设网站凡科官网app下载