临沂做网站哪家好,网站维护专业,做网站用html还是jsp,整站seo优化一般多少钱1.简介
应用启动过程快的都不需要一秒钟#xff0c;但这整个过程的执行是比较复杂的#xff0c;无论是对手机厂商、应用开发来说启动速度也是核心用户体验指标之一#xff0c;本文采用Android14源码与perfetto工具进行解析。
源码参考地址#xff1a;Search
trace分析工…1.简介
应用启动过程快的都不需要一秒钟但这整个过程的执行是比较复杂的无论是对手机厂商、应用开发来说启动速度也是核心用户体验指标之一本文采用Android14源码与perfetto工具进行解析。
源码参考地址Search
trace分析工具Perfetto UI
2. Input事件处理流程
Input 是Android系统最常见的事件驱动之一用户的点击、滑动、长按等操作都属于 input 事件驱动其中跑在 SystemServer进程的两个 native 循环线程InputReader 和 InputDispatcher就是input的核心负责读取和分发 Input 事件。整个处理过程大致流程如下
InputReader负责从EventHub里面把Input事件读取出来后放入 inboundqueue,即“iq”队列然后交给 InputDispatcher 进行事件分发InputDispatcher在拿到 InputReader获取的事件之后对事件进行包装后放入 outboundqueueue,即“oq”队列寻找并分发到各个目标窗口App的事件WaitQueue队列“wq“里面记录的是已经派发给 App的事件但是 App还在处理没有返回处理成功的事件PendingInputEventQueue队列“aq”中记录的是应用需要处理的Input事件这里可以看到input事件已经传递到了应用进程在perfettotrace 中inputreader.inputdispatcher.iq队列oq队列wq队列wq队列都在system_server进程之中每一个应用都有自己的aq队列。deliverInputEvent 标识 App UI Thread 被 Input 事件唤醒App 响应处理Input 事件内部会在其界面View树中传递处理。InputReader从trace里可以看到Input按下去的时候AppLaunch_dispatchPtr:Down和Input抬起的时候AppLaunch_dispatchPtr:Up分析启动流程的时候可以从AppLaunch_dispatchPtr:Up开始
从trace上分析如下 3. 应用进程的创建与启动
3.1 Pause桌面应用
这个过程可以先看一下trace的整体表现然后再看对应的源码流程
3.1.1 trace分析如下 3.1.2 源码分析如下
launcher进程接收到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, ActivityOptions options, Task inTask,TaskFragment inTaskFragment, BalCode int balCode,NeededUriGrants intentGrants, int realCallingUid) {int result START_CANCELED;final Task startedActivityRootTask;// Create a transition now to record the original intent of actions taken within// startActivityInner. Otherwise, logic in startActivityInner could start a different// transition based on a sub-action.// Only do the create here (and defer requestStart) since startActivityInner might abort.....try {//添加startActivityInnertagTrace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, startActivityInner);// 执行startActivityInner启动应用的逻辑result startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, options, inTask, inTaskFragment, balCode,intentGrants, realCallingUid);} finally {Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);startedActivityRootTask handleStartResult(r, options, result, newTransition,remoteTransition);}}
....return result;
}
在启动app前需要检查当前前台resume状态的activity一般为launcher应用所以第一步需要让launcher的 activity 进入pause状态。相关简化代码逻辑如下
/frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,boolean deferPause) {boolean pausing !deferPause taskDisplayArea.pauseBackTasks(next);// mResumedActivity不为null说明当前存在处于resume状态的Activity且不是新需要启动的应用if (mResumedActivity ! null) {ProtoLog.d(WM_DEBUG_STATES, resumeTopActivity: Pausing %s, mResumedActivity);// 执行startPausing通知桌面应用进入paused状态pausing | startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,next, resumeTopActivity);}
}boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,String reason) {
......schedulePauseActivity(prev, userLeaving, pauseImmediately,false /* autoEnteringPip */, reason);
......}void schedulePauseActivity(ActivityRecord prev, boolean userLeaving,boolean pauseImmediately, boolean autoEnteringPip, String reason) {ProtoLog.v(WM_DEBUG_STATES, Enqueueing pending pause: %s, prev);try {
.....// 相关执行动作封装事务binder通知mResumedActivity也就是桌面执行pause动作mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),prev.token, PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately, autoEnteringPip));} catch (Exception e) {// Ignore exception, if process died other code will cleanup.
....}
}桌面应用进程这边执行收到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) {if (mDontReport) {return;}// TODO(lifecycler): Use interface callback instead of actual implementation.ActivityClient.getInstance().activityPaused(token);
}
AMS这边收到应用的activityPaused调用后继续执行启动应用的逻辑判断需要启动的应用Activity所在的进程不存在所以接下来需要先startProcessAsync创建应用进程相关简化代码如下
/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {// Is this activitys application already running?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;}
.......final boolean isTop andResume r.isTopRunningActivity();mService.startProcessAsync(r, knownToBeDead, isTop,isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY: HostingRecord.HOSTING_TYPE_ACTIVITY);
}
3.2 创建应用进程
在桌面点击图标启动一个应用的组件如Activity时如果Activity所在的进程不存在就会创建并启动进程。Android系统中一般应用进程的创建都是统一由zygote进程fork创建的AMS在需要创建应用进程时会通过socket连接并通知到到zygote进程在开机阶段就创建好的socket服务端然后由zygote进程fork创建出应用进程。整体架构如下图所示 应用进程创建流程图.png
3.2.1 AMS 发送socket请求
3.2.1.1 源码分析如下
ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
GuardedBy(this)final ProcessRecord startProcessLocked(...) {return mProcessList.startProcessLocked(...);
}
ProcessList.java
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);}
}
Process.java
frameworks/base/core/java/android/os/Process.java
public static ProcessStartResult start(...) {// 调用ZygoteProcess的start函数return ZYGOTE_PROCESS.start(...);
}
ZygoteProcess.java
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();...
//在ZygoteProcess#startViaZygote中最后创建应用进程的逻辑
1. openZygoteSocketIfNeeded函数中打开本地socket客户端连接到zygote进程的socket服务端
2. zygoteSendArgsAndGetResult发送socket请求参数带上了创建的应用进程参数信息
3. return返回的数据结构ProcessStartResult中会有新创建的进程的pid字段。return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
3.2.1.2 trace分析如下 3.2.2 Zygote 处理socket请求
其实早在系统开机阶段zygote进程创建时就会在ZygoteInit#main入口函数中创建服务端socket并预加载系统资源和框架类加速应用进程启动速度
3.2.2.1 源码分析如下
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);}}}}
继续往下看ZygoteConnection#processCommand如何监听并处理AMS客户端的请求
/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java 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 {...}}}
3.2.3 应用进程初始化
接上一节中的分析zygote进程监听接收AMS的请求fork创建子应用进程然后pid为0时进入子进程空间然后在 ZygoteInit#zygoteInit中完成进程的初始化动作
3.2.3.1 源码分析如下
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);}
我们继续看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中。
主线程初始化完成后主线程就有了完整的 Looper、MessageQueue、Handler此时 ActivityThread 的 Handler 就可以开始处理 Message包括 Application、Activity、ContentProvider、Service、Broadcast 等组件的生命周期函数都会以 Message 的形式在主线程按照顺序处理这就是 App 主线程的初始化和运行原理。
主线程初始化完成后主线程就进入阻塞状态等待 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 等。
3.2.3.2 trace分析如下 4. 应用Application和Activity组件创建与初始化
4.1 Application的创建与初始化
应用进程启动初始化执行ActivityThread#main函数过程中在开启主线程loop消息循环之前会通过Binder调用系统核心服务AMS的attachApplication接口将自己注册到AMS中。下面我们接着这个流程往下看我们先从systrace上看看AMS服务的attachApplication接口是如何处理应用进程的attach注册请求的
trace分析如下 attachApplication.png
我们继续来看相关代码的简化流程
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.makeApplicationInner(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资源的加载流程。
4.1.1 应用APK的Dex文件加载
ContextImpl.java
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,ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName);// 2.触发加载APK的DEX文件和Resource资源context.setResources(packageInfo.getResources());context.mContextType isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI: CONTEXT_TYPE_NON_UI;return context;
}
LoadedApk.java
frameworks/base/core/java/android/app/LoadedApk.java UnsupportedAppUsage
public Resources getResources() {if (mResources null) {...// 加载APK的Resource资源mResources ResourcesManager.getInstance().getResources(null, mResDir,splitPaths, mLegacyOverlayDirs, mOverlayPaths,mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),getClassLoader()//触发加载APK的DEX文件, null);
}
return mResources;UnsupportedAppUsage
public ClassLoader getClassLoader() {synchronized (mLock) {if (mClassLoader null) {createOrUpdateClassLoaderLocked(null /*addedPaths*/);}return mClassLoader;}
}
private void createOrUpdateClassLoaderLocked(ListString addedPaths) {if (!mIncludeCode) {if (mDefaultClassLoader null) {StrictMode.ThreadPolicy oldPolicy allowThreadDiskReads();//创建默认的mDefaultClassLoader对象触发art虚拟机加载dex文件mDefaultClassLoader ApplicationLoaders.getDefault().getClassLoader( /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,librarySearchPath, libraryPermittedPath, mBaseClassLoader,null /* classLoaderName */);setThreadPolicy(oldPolicy);mAppComponentFactory AppComponentFactory.DEFAULT;}}...if (mClassLoader null) {// 赋值给mClassLoader对象mClassLoader mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,new ApplicationInfo(mApplicationInfo));}
}
ApplicationLoaders.java
frameworks/base/core/java/android/app/ResourcesManager.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,nativeSharedLibraries, sharedLibrariesLoadedAfterApp);}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,nativeSharedLibraries, sharedLibrariesLoadedAfterApp);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,null /*sharedLibrariesLoadedAfterApp*/);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);return loader;}
}ClassLoaderFactory.java
frameworks/base/core/java/com/android/internal/os/ClassLoaderFactory.java
public static ClassLoader createClassLoader(...) {ClassLoader[] arrayOfSharedLibraries (sharedLibraries null)? null: sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);if (isPathClassLoaderName(classloaderName)) {return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);}...
}
public static ClassLoader createClassLoader(String dexPath,String librarySearchPath, ClassLoader parent, String classloaderName,ListClassLoader sharedLibraries, ListClassLoader sharedLibrariesLoadedAfter) {// 通过new的方式创建ClassLoader对象最终会触发art虚拟机加载APK的dex文件ClassLoader[] arrayOfSharedLibraries (sharedLibraries null)? null: sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);ClassLoader[] arrayOfSharedLibrariesLoadedAfterApp (sharedLibrariesLoadedAfter null)? null: sharedLibrariesLoadedAfter.toArray(new ClassLoader[sharedLibrariesLoadedAfter.size()]);if (isPathClassLoaderName(classloaderName)) {return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries,arrayOfSharedLibrariesLoadedAfterApp);
.....}
从以上代码可以看出在创建Application的Context对象后会立马尝试去加载APK的Resource资源而在这之前需要通过LoadedApk去创建类加载器ClassLoader对象而这个过程最终就会触发Art虚拟机加载应用APK的dex文件从systrace上看如下图所示 OpenDexFilesFromOat.png
4.1.2 应用APK的Resource资源加载
ResourcesManager.java
frameworks/base/core/java/android/app/ResourcesManager.java
public Nullable Resources getResources(...) {try {// 原生Resource资源加载的systrace tagTrace.traceBegin(Trace.TRACE_TAG_RESOURCES, ResourcesManager#getResources);...resources createResources(key, classLoader, assetsSupplier);}return resources;} 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, flags);}...}
ApkAssets.java
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上看如下图所示 getResources.png
4.2 Activity的创建与初始化
AMS在收到应用进程的attachApplication注册请求后先通过oneway类型的binder调用应用及进程的IApplicationThread#bindApplication接口触发应用进程在主线程执行handleBindeApplication初始化操作然后继续执行启动应用Activity的操作下面我们来看看系统是如何启动创建应用Activity的简化代码流程如下
ActivityManagerService.java
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;}}
}
ActivityTaskManagerService.java
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);}}
}
RootWindowContainer.java
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean attachApplication(WindowProcessController app) throws RemoteException {try {return mAttachApplicationHelper.process(app);} finally {mAttachApplicationHelper.reset();}
}
......private class AttachApplicationHelper implements ConsumerTask, PredicateActivityRecord {private boolean mHasActivityStarted;private RemoteException mRemoteException;private WindowProcessController mApp;private ActivityRecord mTop;try {// realStartActivityLocked真正实现启动应用Activity流程if (mTaskSupervisor.realStartActivityLocked(r, mApp,mTop r r.getTask().canBeResumed(r) /* andResume */,true /* checkConfig */)) {mHasActivityStarted true;}} catch (RemoteException e) {Slog.w(TAG, Exception in new application when starting activity mTop, e);mRemoteException e;return true;}return false;}}
}
ActivityTaskSupervisor.java /frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.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进程最终是通过ActivityTaskSupervisor#realStartActivityLocked函数中通过LaunchActivityItem和ResumeActivityItem两个类的封装依次实现binder调用通知应用进程这边执行Activity的Launch和Resume动作的我们继续往下看相关代码流程
4.2.1 Activity Create
LaunchActivityItem.java
frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java
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, mReferrer, mVoiceInteractor, mState, mPersistentState,mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,mTaskFragmentToken);// 调用到ActivityThread的handleLaunchActivity函数在主线程执行应用Activity的Launch创建动作client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);}
ActivityThread.java
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) {...}
}
Activity.java
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对象
以上过程从trace分析如下 ActivityStart.png
4.2.2 Activity Resume
ResumeActivityItem.java
frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java
Override
public void execute(ClientTransactionHandler client, ActivityClientRecord r,PendingTransactionActions pendingActions) {// 原生标识Activity Resume的systrace tagTrace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, activityResume);client.handleResumeActivity(r, true /* finalStateRequest */, mIsForward,mShouldSendCompatFakeFocus, RESUME_ACTIVITY);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
ActivityThread.java
frameworks/base/core/java/android/app/ActivityThread.java
Override
public void handleResumeActivity(...){...// 1.执行performResumeActivity流程,执行应用Activity的onResume生命周期函数if (!performResumeActivity(r, finalStateRequest, reason)) {return;} ...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);...
}WindowManagerGlobal.java
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界面绘制动作
以上过程从trace分析如下 activityResume.png
5. Choreographer
5.1 Choreographer的作用
Choreographer 的引入主要是配合系统Vsync垂直同步机制在 Android 渲染链路扮演中承上启下的角色
承上负责接收和处理 App 的各种更新消息和回调等到 Vsync 到来的时候统一处理。比如集中处理 Input(主要是 Input 事件的处理) 、Animation(动画相关)、Traversal(包括 measure、layout、draw 等操作) 判断卡顿掉帧情况记录 CallBack 耗时等
启下负责请求和接收 Vsync 信号。接收 Vsync 事件回调(通过 FrameDisplayEventReceiver.onVsync )请求 Vsync(FrameDisplayEventReceiver.scheduleVsync)
从上面可以看出来 Choreographer 担任的是一个工具人的角色他之所以重要是因为通过 Choreographer SurfaceFlinger Vsync TripleBuffer 这一套从上到下的机制保证了 Android App 可以以一个稳定的帧率运行(20fps、90fps 或者 60fps)减少帧率波动带来的不适感。
5.2 ViewRootImpl
接上一节的分析应用主线程中在执行Activity的Resume流程的最后会创建ViewRootImpl对象并调用其setView函数从此并开启了应用界面UI布局与绘制的流程。
我们从ViewRootImpl的setView流程继续结合代码往下看
ViewRootImpl.java
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, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,mTempControls, attachedFrame, compatScale);...// 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(maxTargetSdk Build.VERSION_CODES.R, trackingBug 170729553)
void scheduleTraversals() {if (!mTraversalScheduled) {...// 注意此处会往主线程的MessageQueue消息队列中添加同步栏删因为系统绘制消息属于异步消息需要更高优先级的处理mTraversalBarrier mHandler.getLooper().getQueue().postSyncBarrier();// 通过Choreographer往主线程消息队列添加CALLBACK_TRAVERSAL绘制类型的待执行消息用于触发后续UI线程真正实现绘制动作mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);...}
}
ViewRootImpl会调用Choreographer的postCallback接口放入待执行的绘制消息后Choreographer会先向系统申请APP 类型的vsync信号然后等待系统vsync信号到来后去回调到ViewRootImpl的doTraversal函数中执行真正的绘制动作measure、layout、draw。
我们接着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“画布”操作relayoutResult mWindowSession.relayout(mWindow, params,requestedWidth, requestedHeight, viewVisibility,insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,mTempInsets, mTempControls, mRelayoutBundle);....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软件绘制的流程
5.3 以上过程从trace分析如下 借用一张图来总结应用UI绘制的流程如下所示 UI绘制流程.png
6. RenderThread
目前为止用户依然看不到屏幕上显示的应用界面内容因为整个Android系统的显示流程除了前面讲到的UI线程的绘制外界面还需要经过RenderThread线程的渲染处理渲染完成后还需要通过Binder调用“上帧”交给surfaceflinger进程中进行合成后送显才能最终显示到屏幕上。
我们将接上一节中ViewRootImpl中最后draw的流程继续往下分析开启硬件加速情况下RenderThread渲染线程的工作流程。由于目前Android 4.X之后系统默认界面是开启硬件加速的所以本文我们重点分析硬件加速条件下的界面渲染流程我们先分析一下简化的代码流程
6.1 硬件加速绘制
ViewRootImpl.java
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 {...}
}ThreadedRenderer.java
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(frameInfo);...
}
从上面的代码可以看出硬件加速绘制主要包括两个阶段
从DecorView根节点出发递归遍历View控件树记录每个View节点的drawOp绘制操作命令完成绘制操作命令树的构建JNI调用同步Java层构建的绘制命令树到Native层的RenderThread渲染线程并唤醒渲染线程利用OpenGL执行渲染任务
6.2 构建绘制命令树
我们先来看看第一阶段构建绘制命令树的代码简化流程
6.2.1 源码分析如下
ThreadedRenderer.java
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();...
}
View.java
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();...}...
}CallSuper
public void draw(NonNull Canvas canvas) {...// draw the content(View自己实现的onDraw绘制由应用开发者自己实现)onDraw(canvas);...// draw the childrendispatchDraw(canvas);...
}
RenderNode.java
frameworks/base/graphics/java/android/graphics/RenderNode.java
public void endRecording() {if (mCurrentRecordingCanvas null) {throw new IllegalStateException(No recording in progress, forgot to call #beginRecording()?);}RecordingCanvas canvas mCurrentRecordingCanvas;mCurrentRecordingCanvas null;// 从SkiaRecordingCanvas中获取SkiaDisplayList对象canvas.finishRecording(this);// 将SkiaDisplayList对象填充到RenderNode中canvas.recycle();
}
从以上代码可以看出构建绘制命令树的过程是从View控件树的根节点DecorView触发递归调用每个子View节点的updateDisplayListIfDirty函数最终完成绘制树的创建简述流程如下
利用View对象构造时创建的RenderNode获取一个SkiaRecordingCanvas“画布”利用SkiaRecordingCanvas在每个子View控件的onDraw绘制函数中调用drawLine、drawRect等绘制操作时创建对应的DisplayListOp绘制命令并缓存记录到其内部的SkiaDisplayList持有的DisplayListData中将包含有DisplayListOp绘制命令缓存的SkiaDisplayList对象设置填充到RenderNode中最后将根View的缓存DisplayListOp设置到RootRenderNode中完成构建。
6.2.2 以上过程从trace分析如下 构建View绘制命令树.png
6.3 执行渲染绘制任务
经过上一小节中的分析应用在UI线程中从根节点DecorView出发递归遍历每个子View节点搜集其drawXXX绘制动作并转换成DisplayListOp命令将其记录到DisplayListData并填充到RenderNode中最终完成整个View绘制命令树的构建。从此UI线程的绘制任务就完成了。下一步UI线程将唤醒RenderThread渲染线程触发其利用OpenGL执行界面的渲染任务本小节中我们将重点分析这个流程。
6.3.1 源码分析如下
HardwareRenderer.java
frameworks/base/graphics/java/android/graphics/HardwareRenderer.java
public int syncAndDrawFrame(NonNull FrameInfo frameInfo) {// JNI调用native层的相关函数return nSyncAndDrawFrame(mNativeProxy, frameInfo.frameInfo, frameInfo.frameInfo.length);
}
android_graphics_HardwareRenderer.cpp
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();
}
RenderProxy.cpp
frameworks/base/libs/hwui/renderthread/RenderProxy.cpp
int RenderProxy::syncAndDrawFrame() {// 唤醒RenderThread渲染线程执行DrawFrame绘制任务return mDrawFrameTask.drawFrame();
}
DrawFrameTask.cpp
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线程// From this point on anything in this is *UNSAFE TO ACCESS*if (canUnblockUiThread) {unblockUiThread();}...if (CC_LIKELY(canDrawThisFrame)) {// 2.执行draw渲染绘制动作context-draw(solelyTextureViewUpdates);} else {...}...
}bool DrawFrameTask::syncFrameState(TreeInfo info) {ATRACE_CALL();...// 调用CanvasContext的prepareTree函数实现绘制命令树同步的流程mContext-prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);...
}
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);...}...
}
RenderNode.cpp
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,[this](RenderNode* child, TreeObserver observer, TreeInfo info,bool functorsNeedLayer) {child-prepareTreeImpl(observer, info, functorsNeedLayer);mHasHolePunches | child-hasHolePunches();});
.....}.
}void RenderNode::pushStagingDisplayListChanges(TreeObserver observer, TreeInfo info) {...syncDisplayList(observer, info);...
}void RenderNode::syncDisplayList(TreeObserver observer, TreeInfo* info) {...// 完成赋值同步DisplayList对象deleteDisplayList(observer, info);mDisplayList std::move(mStagingDisplayList);...
}
CanvasContext.cpp
/frameworks/base/libs/hwui/renderthread/CanvasContext.cpp
void CanvasContext::draw() {...// 1.调用OpenGL库使用GPU按照构建好的绘制命令完成界面的渲染drawResult mRenderPipeline-draw(frame, windowDirty, dirty, mLightGeometry,mLayerUpdateQueue, mContentDrawBounds, mOpaque,mLightInfo, mRenderNodes, (profiler()), mBufferParams);...// 2.将前面已经绘制渲染好的图形缓冲区Binder上帧给SurfaceFlinger合成和显示bool didSwap mRenderPipeline-swapBuffers(frame, drawResult.success, windowDirty,mCurrentFrameInfo, requireSwap);
}
从以上代码可以看出UI线程利用RenderProxy向RenderThread线程发送一个DrawFrameTask任务请求RenderThread被唤醒开始渲染大致流程如下
syncFrameState中遍历View树上每一个RenderNode执行prepareTreeImpl函数实现同步绘制命令树的操作调用OpenGL库API使用GPU按照构建好的绘制命令完成界面的渲染具体过程由于本文篇幅所限暂不展开分析将前面已经绘制渲染好的图形缓冲区Binder上帧给SurfaceFlinger合成和显示
6.3.2 以上过程从trace分析如下 RenderThread实现界面渲染.png
7. SurfaceFlinger合成显示
SurfaceFlinger合成显示部分完全属于Android系统GUI中图形显示的内容逻辑结构也比较复杂但不属于本文介绍内容的重点。所以本小节中只是总体上介绍一下其工作原理与思想不再详细分析源码感兴趣的读者可以关注笔者后续的文章再来详细分析讲解。简单的说SurfaceFlinger作为系统中独立运行的一个Native进程借用Android官网的描述其职责就是负责接受来自多个来源的数据缓冲区对它们进行合成然后发送到显示设备。如下图所示 SurfaceFlinger工作原理.jpg 从上图可以看出其实SurfaceFlinger在Android系统的整个图形显示系统中是起到一个承上启下的作用
对上通过Surface与不同的应用进程建立联系接收它们写入Surface中的绘制缓冲数据对它们进行统一合成。对下通过屏幕的后缓存区与屏幕建立联系发送合成好的数据到屏幕显示设备。
图形的传递是通过Buffer作为载体Surface是对Buffer的进一步封装也就是说Surface内部具有多个Buffer供上层使用如何管理这些Buffer呢答案就是BufferQueue 下面我们来看看BufferQueue的工作原理
7.1 BufferQueue机制
借用一张经典的图来描述BufferQueue的工作原理 BufferQueue状态转换图.jpg 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中。
7.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信号的生成是参考屏幕硬件的刷新周期的其架构如下图所示 vsync.png
7.3 trace上SurfaceFlinger工作的流程如下图所示 SurfaceFlinger处理.png
8.总结
本文结合Android 14源码和Perfetto分析了从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面第一帧画面的完整流程这其中涉及了App应用、system_server框架、surfaceflinger等一系列Android系统核心模块的相互配合有很多的细节也由于篇幅所限无法完全展开分析感兴趣的读者可以结合AOSP源码继续深入分析。而优化应用启动打开的速度这个系统核心用户体验的指标也是多少年来谷歌、SOC芯片厂商、ODM手机厂商以及各个应用开发者共同努力优化的方向
对于SOC芯片厂商而言需要不断升级CPU和GPU的硬件算力对于Android系统的维护者谷歌而言在Android系统大版本升级过程中不断的优化应用启动过程上的各个系统流程比如进程创建的速度优化、Art虚拟机的引入与性能优化、View绘制流程的简化、硬件绘制加速机制的引入、系统核心AMS、WMS等核心服务的锁优化等对于各个ODM手机厂商而言开发识别应用启动的场景进行针对性的CPU主频的拉升调节、触控响应速度的优化等机制对于各个应用开发者而言会结合自己的业务对应用启动的场景进行优化比如尽量减少或推迟在Application、Activity生命周期函数中的初始化逻辑、去除界面布局的过度绘制、异步化的布局XML文件解析等机制。
9.参考文档
Search
Android应用启动全流程分析源码深度剖析 - 简书
理解Android硬件加速原理的小白文 - 简书
Perfetto详细解析-CSDN博客
https://www.androidperformance.com/2021/04/24/android-systrace-smooth-in-action-1/ 史上最全Android渲染机制讲解长文源码深度剖析https://mp.weixin.qq.com/s?__bizMzU2MTk0ODUxOQmid2247483782idx1snf9eae167b217c83036b3a24cd4182cd1chksmfc71b38ecb063a9847f4518802fc541091d7f708b112399ec39827e68a6f590249748d643747mpshare1scene1srcid0224RGsfWeG5GyMpxLwEhx7Nsharer_sharetime1582507745901sharer_shareid2d76fc4769fc55b6ca84ec3820ba5821#rd