【Android进阶】APP冷启动流程解析

本文为launcher图标拉起app的全流程解析
从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面而完成应用启动,快的话往往都不需要一秒钟,但是这整个过程却是十分复杂的,其中涉及了Android系统的几乎所有核心知识点。
同时应用的启动速度也绝对是系统的核心用户体验指标之一,多少年来,无论是谷歌或是手机系统厂商们还是各个Android应用开发者,都在为实现应用打开速度更快一点的目标而不断努力。
概要图

流程详解
事件传递到launcher图标view
手指按下后,硬件到驱动到系统侧链路暂且不看。
System部分
Android 系统是由事件驱动的,而 input 是最常见的事件之一,用户的点击、滑动、长按等操作,都属于 input 事件驱动,其中的核心就是 InputReader 和 InputDispatcher 。 InputReader 和 InputDispatcher 是跑在 SystemServer进程中的两个 native 循环线程,负责读取和分发 Input 事件。
InputReader负责从EventHub里面把 Input事件 读取出来,然后交给InputDispatcher进行事件分发;InputDispatcher在拿到InputReader获取的事件之后,对事件进行包装后,寻找并分发到目标窗口;
system_server 的native线程 InputReader 读取到了一个触控事件。它会唤醒 InputDispatcher 去进行事件分发,先放入 InboundQueue 队列中,再去寻找处理事件的窗口,找到窗口后就会放入 OutboundQueue 队列,等待通过socket通信发送到 launcher应用 的窗口中,此时事件处于 WaitQueue 中,等待事件被处理,若5s内没有处理,就会向 systemserver 报ANR异常。

Launcher部分
Launcher进程接收到之后,通过 enqueueInputEvent 函数放入 “aq” 本地待处理队列中,唤醒 UI线程 的 deliverInputEvent 流程进行事件分发处理,具体交给界面window里的类型来处理。
从View布局树的根节点DecorView开始遍历整个View树上的每一个子View或ViewGroup界面进行事件的分发、拦截、处理的逻辑。
这次的触摸事件被消耗后,Launcher及时调用 finishInputEvent 结束应用的处理逻辑,再通过JNI调用到native层InputConsumer的 sendFinishedSignal 函数通知 InputDispatcher 事件处理完成,及时从 waitqueue 里移除待处理事件,避免ANR异常。
整个处理流程是按照责任链的设计模式进行
Launcher到AMS的binder调用
上一轮 Input事件 传到图标view后,通过一个 ACTION_DOWN 的 TouchEvent 触控事件和多个 ACTION_MOVE 事件,直到最后出现一个 ACTION_UP 的 TouchEvent 事件后,去判断是 click 点击事件。
就开始通过 ActivityManager Binder 调用AMS的 startActivity 服务接口准备启动应用。
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
...
try {
...
// 添加“startActivityInner”的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
// 执行startActivityInner启动应用的逻辑
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
...
}
...
}
上面AMS的 startActivityUnchecked 函数,开始和结尾都会添加traceTAG记录时间,中间则是调用 startActivityInner 方法来启动应用。这个方法首 先检查当前Activity栈里处于resume状态的Activity ,如果当前 mResumedActivity 不是目标Activity,就通知这个Activity 进入Pause状态 。
/*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) {
...
}
}
...
}
Launcher进程把其Activity的 pause 操作执行完毕后,执行 ActivityTaskManager.getService().activityPaused(token) 会将pause完成的结果通知到AMS。
AMS通知Launcher暂停自己的之后,会继续启动应用的逻辑,不等待Launcher进程的pause处理结果。
- 先判断需要启动应用进程如果存在,调用
realStartActivityLocked - 如果进程不存在,就会先调用
startProcessAsync创建进程。
拉起一个应用进程,具体是AMS通过Socket连接到Zygote进程,后者在开机时会创建好一个服务端。AMS通知Zygote进程去fork一个新进程,即 ZygoteProcess.start(...) 方法。
Zygote开机时就会创建 ZygoteServer 对象,调用 runSelectLoop 进入死循环等待AMS的请求。
/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/
@GuardedBy("this")
final ProcessRecord startProcessLocked(...) {
return mProcessList.startProcessLocked(...);
}
/*frameworks/base/services/core/java/com/android/server/am/ProcessList.java*/
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
int mountExternal, String seInfo, String requiredAbi, String instructionSet,
String invokeWith, long startTime) {
try {
// 原生标识应用进程创建所加的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
...
// 调用Process的start方法创建进程
startResult = Process.start(...);
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
/*frameworks/base/core/java/android/os/Process.java*/
public static ProcessStartResult start(...) {
// 调用ZygoteProcess的start函数
return ZYGOTE_PROCESS.start(...);
}
/*frameworks/base/core/java/android/os/ZygoteProcess.java*/
public final Process.ProcessStartResult start(...){
try {
return startViaZygote(...);
} catch (ZygoteStartFailedEx ex) {
...
}
}
private Process.ProcessStartResult startViaZygote(...){
ArrayList<String> argsForZygote = new ArrayList<String>();
...
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
在 ZygoteProcess#startViaZygote 函数中,拿到创建进程的参数,返回一个列表,里面含有pid等信息。
startProcess 中会封装相关进程信息请求参数,连接发送到zygote进程的socket服务端最后阻塞等待进程创建的结果。 startProcess 的阻塞工作线程,最终被711线程也就是zygote进程的主线程唤醒。
Zygote进程fork应用进程

ZygoteServer 接收到请求后,去fork一个进程,fork采用 copy-on-write技术,这是linux创建进程的标准方法,调用一次,返回两次,返回值有3种类型,父进程里是新的子进程的pid,子进程返回的是0,为负数则表示出错了。
父进程去把pid通过socket发送到AMS,子进程通过调用 handleChildProc 函数,关闭父进程继承来的服务地址,再做一些通用的初始化工作,比如启用Binder机制,执行应用程序的入口函数。
子进程里有三个重要方法:
- 应用进程默认的java异常处理机制(可以实现监听、拦截应用进程所有的Java crash的逻辑);
- JNI调用启动进程的binder线程池(注意应用进程的binder线程池资源是自己创建的并非从zygote父进程继承的);
- 最后通过
RuntimeInit#applicationInit中 反射创建ActivityThread对象 并调用其“main”入口方法。进入到子进程内部逻辑。
/*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);
}
目标APP的内部逻辑
建立消息机制
ActivityThread 对象的 main() 方法,里面主要分两步。
- 一是创建主线程并
prepare()来启动消息循环; - 二是通过binder调用AMS的
attachApplication接口,将自己注册到AMS中。
继续分析主线程消息循环机制的建立, Looper.prepareMainLooper() ,通过prepare,创建MassageQueue队列,准备主线程的Looper,通过ThreadLocal机制实现与主线程的一对一绑定。
/*frameworks/base/core/java/android/app/ActivityThread.java*/
public static void main(String[] args) {
...
// 1.创建Looper、MessageQueue
Looper.prepareMainLooper();
...
// 2.启动loop消息循环,开始准备接收消息
Looper.loop();
...
}
// 3.创建主线程Handler对象
final H mH = new H();
class H extends Handler {
...
}
/*frameworks/base/core/java/android/os/Looper.java*/
public static void prepareMainLooper() {
// 准备主线程的Looper
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建主线程的Looper对象,并通过ThreadLocal机制实现与主线程的一对一绑定
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
// 创建MessageQueue消息队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
以上一切完成后,主线程就有了完整的 Looper、MessageQueue、Handler,此时 ActivityThread 的 Handler 就可以开始处理 Message。
主线程的初始化完成后,主线程就进入阻塞状态,等待 Message,一旦有 Message 发过来,主线程就会被唤醒,处理 Message,处理完成之后,如果没有其他的 Message 需要处理,那么主线程就会进入休眠阻塞状态继续等待。
包括 Application、Activity、ContentProvider、Service、Broadcast 等组件的生命周期函数,都会以 Message 的形式,在主线程按照顺序处理。
Looper循环器,其loop方法开启后,不断地从MessageQueue中获取Message;MessageQueue 就是一个 Message 管理器,队列中是 Message,在没有 Message 的时候,MessageQueue借助Linux的ePoll机制,阻塞休眠等待,直到有Message进入队列将其唤醒。
Message 是传递消息的对象,其内部包含了要传递的内容,最常用的包括 what、arg、callback 等。
将自己的进程注册到AMS
上面是应用内的消息机制建立和初始化,看看AMS怎么处理这个进程的 attach 注册请求的。AMS接收到请求后,通过 oneway类型 的binder调用此进程的 bindApplication 接口,里面会往主线程的消息队列中post一个 BIND_APPLICATION 的消息,触发主线程的 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);
...
}
这个方法里通过AMS发过来的 ApplicationInfo ,创建LoadedApk对象;创建 Application 的 Context ;触发Art虚拟机加载应用APK的Dex文件到内存中;通过 LoadedApk 加载应用的Resource资源;LoadedApk 的 makeApplication 方法创建 Application 对象;
// /frameworks/base/core/java/android/app/Instrumentation.java
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
然后执行 Application 的 attachBaseContext 方法,通过 installContentProviders 创建 ContentProvider ,执行其 onCreate 方法,随后执行 Application 的 onCreate 方法。
Dex文件加载
背景:Java代码在JVM被编译成字节码,再翻译成机器语言来运行。而DVM即Dalvik虚拟机不能和JVM一样能直接运行Java字节码,它只能运行.dex文件。dex文件是由Java的字节码通过Android的dx生成工具来生成的,这个过程就是打包apk的流程。
后面5.0后推出的ART虚拟机,相比Dalvik的JIT实时编译,是在启动时将dex转换成机器码,ART采用了AOT预先编译,在安装apk时就把dex文件转换成可以直接运行的oat文件,其可以支持多dex,大幅提升冷启动速度。缺点是安装速度变慢。
/*frameworks/base/core/java/android/app/ContextImpl.java*/
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
// 1.创建应用Application的Context对象
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
0, null, opPackageName);
// 2.触发加载APK的DEX文件和Resource资源
context.setResources(packageInfo.getResources());
context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
return context;
}
/*frameworks/base/core/java/android/app/LoadedApk.java*/
@UnsupportedAppUsage
public Resources getResources() {
if (mResources == null) {
...
// 加载APK的Resource资源
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
getClassLoader()/*触发加载APK的DEX文件*/, null);
}
return mResources;
}
@UnsupportedAppUsage
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader == null) {
createOrUpdateClassLoaderLocked(null /*addedPaths*/);
}
return mClassLoader;
}
}
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
...
if (mDefaultClassLoader == null) {
...
// 创建默认的mDefaultClassLoader对象,触发art虚拟机加载dex文件
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
mApplicationInfo.classLoaderName, sharedLibraries);
...
}
...
if (mClassLoader == null) {
// 赋值给mClassLoader对象
mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
new ApplicationInfo(mApplicationInfo));
}
}
/*frameworks/base/core/java/android/app/ApplicationLoaders.java*/
ClassLoader getClassLoaderWithSharedLibraries(...) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
}
private ClassLoader getClassLoader(String zip, ...) {
...
synchronized (mLoaders) {
...
if (parent == baseParent) {
...
// 1.创建BootClassLoader加载系统框架类,并增加相应的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
...
return classloader;
}
// 2.创建PathClassLoader加载应用APK的Dex类,并增加相应的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
ClassLoader loader = ClassLoaderFactory.createClassLoader(
zip, null, parent, classLoaderName, sharedLibraries);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return loader;
}
}
/*frameworks/base/core/java/com/android/internal/os/ClassLoaderFactory.java*/
public static ClassLoader createClassLoader(...) {
// 通过new的方式创建ClassLoader对象,最终会触发art虚拟机加载APK的dex文件
ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
? null
: sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
if (isPathClassLoaderName(classloaderName)) {
return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);
}
...
}
上一轮的Context对象创建后,通过 packageInfo.getResources() 去加载加载APK的 Resource 资源赋给 context ,这个方法中需要 getClassLoader 获取类加载器,触发ART虚拟机加载dex文件。
资源文件加载
/*frameworks/base/core/java/android/app/LoadedApk.java*/
@UnsupportedAppUsage
public Resources getResources() {
if (mResources == null) {
...
// 加载APK的Resource资源
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
getClassLoader()/*触发加载APK的DEX文件*/, null);
}
return mResources;
}
/*frameworks/base/core/java/android/app/ResourcesManager.java*/
public @Nullable Resources getResources(...) {
try {
// 原生Resource资源加载的systrace tag
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
...
return createResources(activityToken, key, classLoader, assetsSupplier);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
private @Nullable Resources createResources(...) {
synchronized (this) {
...
// 执行创建Resources资源对象
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
if (resourcesImpl == null) {
return null;
}
...
}
}
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
...
impl = createResourcesImpl(key, apkSupplier);
...
}
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
...
// 创建AssetManager对象,真正实现的APK文件加载解析动作
final AssetManager assets = createAssetManager(key, apkSupplier);
...
}
private @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
...
for (int i = 0, n = apkKeys.size(); i < n; i++) {
final ApkKey apkKey = apkKeys.get(i);
try {
// 通过loadApkAssets实现应用APK文件的加载
builder.addApkAssets(
(apkSupplier != null) ? apkSupplier.load(apkKey) : loadApkAssets(apkKey));
} catch (IOException e) {
...
}
}
...
}
private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
...
if (key.overlay) {
...
} else {
// 通过ApkAssets从APK文件所在的路径去加载
apkAssets = ApkAssets.loadFromPath(key.path,
key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
}
...
}
/*frameworks/base/core/java/android/content/res/ApkAssets.java*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
throws IOException {
return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
}
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
...
// 通过JNI调用Native层的系统system/lib/libandroidfw.so库中的相关C函数实现对APK文件压缩包的解析与加载
mNativePtr = nativeLoad(format, path, flags, assets);
...
}
系统对于应用APK文件资源的加载过程其实就是创建应用进程中的 Resources 资源对象的过程,其中真正实现APK资源文件的I/O解析作,最终是借助于 AssetManager 中通过JNI调用系统Native层的相关C函数实现。
加载应用的 Resource 。上面 getResources() 方法里,创建 ResourcesImpl 时,会调用到 createAssetManager 方法, AssetManager 这是实际加载解析apk的类,通过路径去加载APK文件压缩包。
ApkAssets.loadFromPath(key.path, key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0)
通过JNI调用Native层的系统 system/lib/libandroidfw.so 库中的相关C函数实现对APK文件压缩包的解析与加载。
创建Activity
上面 AMS 接收到新进程的 Application 绑定请求之后,反馈其 bindApplication 接口后,立即开始执行启动Activity的流程。简要流程是框架 system_server 进程最终是通过 ActivityStackSupervisor#realStartActivityLocked 函数中,通过 LaunchActivityItem 和 ResumeActivityItem 两个类的封装,依次实现 binder调用 通知应用进程这边执行 Activity 的 Launch 和 Resume 动作。
/*frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java*/
@GuardedBy("this")
private boolean attachApplicationLocked(...) {
...
if (app.isolatedEntryPoint != null) {
...
} else if (instr2 != null) {
// 1.通过oneway异步类型的binder调用应用进程ActivityThread#IApplicationThread#bindApplication接口
thread.bindApplication(...);
} else {
thread.bindApplication(...);
}
...
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
// 2.继续执行启动应用Activity的流程
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
}
/*frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java*/
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLockWithoutBoost) {
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
// 原生标识attachApplication过程的systrace tag
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
}
try {
return mRootWindowContainer.attachApplication(wpc);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
/*frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java*/
boolean attachApplication(WindowProcessController app) throws RemoteException {
...
final PooledFunction c = PooledLambda.obtainFunction(
// startActivityForAttachedApplicationIfNeeded执行启动应用Activity流程
RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
PooledLambda.__(ActivityRecord.class), app,
rootTask.topRunningActivity());
...
}
private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,
WindowProcessController app, ActivityRecord top) {
...
try {
// ActivityStackSupervisor的realStartActivityLocked真正实现启动应用Activity流程
if (mStackSupervisor.realStartActivityLocked(r, app,
top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) {
...
}
} catch (RemoteException e) {
..
}
}
/*frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java*/
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
...
// 1.先通过LaunchActivityItem封装Binder通知应用进程执行Launch Activity动作
clientTransaction.addCallback(LaunchActivityItem.obtain(...));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
// 2.再通过ResumeActivityItem封装Binder通知应用进程执行Launch Resume动作
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
}
...
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// 执行以上封装的Binder调用
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
...
}
主线程调用到 ActivityThread 的 handleLaunchActivity 函数在主线程执行应用 Activity 的 Launch 创建动作,这个方法里会执行 performLaunchActivity(r, customIntent) ,其中创建 Activity 的 Context ,通过反射创建 activity 对象,再调用其 attach 方法,创建 应用窗口 的 PhoneWindow 对象,并配置 WindowManager 。然后通过 mInstrumentation.callActivityOnCreate(activity, r.state) 执行其 onCreate() 周期,在 setContentView 调用中创建DecorView对象。
Activity和窗口创建完成后, ActivityThread 调用 handleResumeActivity 来执行其 onResume() 流程,在 Activity 的 onResume() 周期回调之后,执行 makeVisible() 。
然后 WindowManager 执行 addView 动作,开启视图绘制逻辑,创建 ViewRootImpl 对象,并调用其 setView 方法。
/*frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java*/
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
// 原生标识Activity Resume的systrace tag
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
"RESUME_ACTIVITY");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
/*frameworks/base/core/java/android/app/ActivityThread.java*/
@Override
public void handleResumeActivity(...){
...
// 1.执行performResumeActivity流程,执行应用Activity的onResume生命周期函数
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
if (r.window == null && !a.mFinished && willBeVisible) {
...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
...
// 2.执行WindowManager#addView动作开启视图绘制逻辑
wm.addView(decor, l);
} else {
...
}
}
}
...
}
public ActivityClientRecord performResumeActivity(...) {
...
// 执行应用Activity的onResume生命周期函数
r.activity.performResume(r.startsNotResumed, reason);
...
}
/*frameworks/base/core/java/android/view/WindowManagerGlobal.java*/
public void addView(...) {
// 创建ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
...
try {
// 执行ViewRootImpl的setView函数
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
...
}
}
setView() 内部会开启硬件加速,调用 requestLayout 来触发界面绘制(measure、layout、draw)动作。通过 Binder 调用 WMS 的 addView 操作,注册应用窗口,创建 WindowInputEventReceiver 对象,传入本地创建 inputChannel 对象用于后续接收系统的触控事件。最后将 DocorView 的 parent 设置为自己,所以 ViewRootImpl 不是一个View,但它是所有 View 的顶层 Parent 。
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
}
...
// 开启绘制硬件加速,初始化RenderThread渲染线程运行环境
enableHardwareAcceleration(attrs);
...
// 1.触发绘制动作
requestLayout();
...
inputChannel = new InputChannel();
...
// 2.Binder调用访问系统窗口管理服务WMS接口,实现addWindow添加注册应用窗口的操作,并传入inputChannel用于接收触控事件
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
...
// 3.创建WindowInputEventReceiver对象,实现应用窗口接收触控事件
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
...
// 4.设置DecorView的mParent为ViewRootImpl
view.assignParent(this);
...
}
}
插入 Activity层级结构和相关概念

- Window是一个抽象类,通过控制DecorView提供了一些标准的UI方案,比如背景、标题、虚拟按键等,而
PhoneWindow是Window的唯一实现类,在Activity创建后的attach流程中创建,应用启动显示的内容装载到其内部的mDecor(DecorView); DecorView是整个界面布局View控件树的根节点,通过它可以遍历访问到整个View控件树上的任意节点;WindowManager是一个接口,继承自ViewManager接口,提供了View的基本操作方法;WindowManagerImp实现了WindowManager接口,内部通过组合方式持有WindowManagerGlobal,用来操作View;WindowManagerGlobal是一个全局单例,内部可以通过ViewRootImpl将View添加至窗口中;ViewRootImpl是所有View的Parent,用来总体管理View的绘制以及与系统WMS窗口管理服务的IPC交互从而实现窗口的开辟;ViewRootImpl是应用进程运转的发动机,可以看到ViewRootImpl内部包含 mView(DecorView)、mSurface、Choregrapher,mView 代表整个控件树,mSurfacce代表画布,应用的UI渲染会直接放到mSurface中,Choregorapher使得应用请求vsync信号,接收信号后开始渲染流程;
View绘制流程
我们的手机屏幕刷新频率有不同的类型,60Hz、120Hz等。60Hz表示屏幕在一秒内刷新60次,也就是每隔16.6ms刷新一次。屏幕会在每次刷新的时候发出一个 VSYNC 信号,通知CPU进行绘制计算。具体到我们的代码中,可以认为就是执行onMesure()、onLayout()、onDraw()这些方法。
同步栏删
requestLayout() 首先进行线程检查,然后给主线程 MessageQueue 队列里增加同步栏删,保证卡住同步消息,只让异步消息通过,直到 VSYNC 信号到来才会执行绘制任务并移除同步屏障。这样可以使绘制消息属于高优先级。
这样在等待 VSYNC 信号的时候主线程什么事都没干?是的。这样的好处是:保证在 VSYNC 信号到来之时,绘制任务可以被及时执行,不会造成界面卡顿。但这样也带来了相对应的代价:
我们的同步消息最多可能被延迟一帧的时间,也就是16ms,才会被执行
主线程Looper造成过大的压力,在VSYNC信号到来之时,才集中处理所有消息
改善这个问题办法就是:使用异步消息。当我们发送异步消息到MessageQueue中时,在等待VSYNC期间也可以执行我们的任务,让我们设置的任务可以更快得被执行且减少主线程Looper的压力。
可能有读者会觉得,异步消息机制本身就是为了避免界面卡顿,那我们直接使用异步消息,会不会有隐患?这里我们需要思考一下,什么情况的异步消息会造成界面卡顿:异步消息任务执行过长、异步消息海量。
如果异步消息执行时间太长,那不管异步还是同步任务,都会造成界面卡顿。
其次,若异步消息海量到达影响界面绘制,那么即使是同步任务,也是会导致界面卡顿的;
原因是MessageQueue是一个链表结构,海量的消息会导致遍历速度下降,也会影响异步消息的执行效率。所以我们应该注意的一点是:
不可在主线程执行重量级任务,无论异步还是同步。
最后建议的使用方案为,如果需要保证与绘制任务的顺序,使用同步Handler;其他,使用异步Handler。
继续看 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);
...
}
}
通过 mChoreographer.postCallback ,往主线程消息队列添加 CALLBACK_TRAVERSAL 绘制类型的待执行消息,用于触发后续UI线程真正实现绘制动作。
Choreographer背景
Choreographer,编舞者,配合系统的VSync垂直同步机制,每次VSync信号到来,就绘制一帧,给app的渲染提供一个稳定的Message处理时机。其在渲染链路中承上启下,统筹处理app的消息和回调,输入事件,动画,Traversal等,到下一次Vsync信号来的时候统一处理,对下他负责接收和请求VSync信号。ViewRootImpl推送待执行的消息之后,Choreographer向系统申请APP的VSync信号,等待信号到来之后,调用到 doTraversal 方法去执行真正的绘制操作。
Vysnc垂直同步
是Android在“黄油计划”中引入的一个重要机制,本质上是为了协调BufferQueue的应用生产者生成UI数据动作和SurfaceFlinger消费者的合成消费动作,避免出现画面撕裂的Tearing现象。Vysnc信号分为两种类型:
- app类型的Vsync:app类型的Vysnc信号由上层应用中的Choreographer根据绘制需求进行注册和接收,用于控制应用UI绘制上帧的生产节奏。根据第7小结中的分析:应用在UI线程中调用invalidate刷新界面绘制时,需要先透过Choreographer向系统申请注册app类型的Vsync信号,待Vsync信号到来后,才能往主线程的消息队列放入待绘制任务进行真正UI的绘制动作;
- sf类型的Vsync:sf类型的Vsync是用于控制SurfaceFlinger的合成消费节奏。应用完成界面的绘制渲染后,通过Binder调用queueBuffer接口将缓存数据返还给应用对应的BufferQueue时,会申请sf类型的Vsync,待SurfaceFlinger 在其UI线程中收到 Vsync 信号之后,便开始进行界面的合成操作。
doTraversal绘制操作
/*frameworks/base/core/java/android/view/ViewRootImpl.java*/
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 调用removeSyncBarrier及时移除主线程MessageQueue中的Barrier同步栏删,以避免主线程发生“假死”
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
// 执行具体的绘制任务
performTraversals();
...
}
}
private void performTraversals() {
...
// 1.从DecorView根节点出发,遍历整个View控件树,完成整个View控件树的measure测量操作
windowSizeMayChange |= measureHierarchy(...);
...
if (mFirst...) {
// 2.第一次执行traversals绘制任务时,Binder调用访问系统窗口管理服务WMS的relayoutWindow接口,实现WMS计算应用窗口尺寸并向系统surfaceflinger正式申请Surface“画布”操作
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
}
...
// 3.从DecorView根节点出发,遍历整个View控件树,完成整个View控件树的layout测量操作
performLayout(lp, mWidth, mHeight);
...
// 4.从DecorView根节点出发,遍历整个View控件树,完成整个View控件树的draw测量操作
performDraw();
...
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
// 通过Binder IPC访问系统WMS服务的relayout接口,申请Surface“画布”操作
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize, mBlastSurfaceControl);
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
// 本地Surface对象获取指向远端分配的Surface的引用
mSurface.copyFrom(mSurfaceControl);
} else {
...
}
}
...
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
// 原生标识View树的measure测量过程的trace tag
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// 从mView指向的View控件树的根节点DecorView出发,遍历访问整个View树,并完成整个布局View树的测量工作
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void performDraw() {
...
boolean canUseAsync = draw(fullRedrawNeeded);
...
}
private boolean draw(boolean fullRedrawNeeded) {
...
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
...
// 如果开启并支持硬件绘制加速,则走硬件绘制的流程(从Android 4.+开始,默认情况下都是支持跟开启了硬件加速的)
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// 否则走drawSoftware软件绘制的流程
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
View绘制三大步,测量,布局,绘制。
首先需要移除同步栏删,removeSyncBarrier,避免主线程接受不了同步消息假死。
然后再执行具体绘制,从DecorView根节点遍历整个View树,完成measure操作。首次执行traversal操作时,通过Binder调用WMS的relayout接口,实现WMS计算窗口尺寸,向系统的surfaceflinger申请Surface画布操作,再由本地surface获取远端分配的surface的引用。画布有了准备进行layout布局,同样从DecorView根节点遍历,完成布局操作。最后的绘制如果开启了硬件加速,则走GPU硬件绘制,否则走CPU软件绘制。
绘制三大步
Measure 测量流程
在Measure测量的时候,会用到一个 MeasureSpec 类,这个类内部的一个32位的int值,其中高2位代表了 SpecMode ,低30位则代表 SpecSize 。 SpecMode 指的是测量模式, SpecSize 指的是测量大小。通过位运算来给这个常量的高2位赋值, SpecMode 有三种情况:
- 00—UNSPECIFIED:未指定模式,View想多大就多大,父容器不做限制,一般用于系统内部的测量。
- 11—- AT_MOST:最大模式,对应于 wrap_content` 属性,子View的最终大小是父View指定的SpecSize值,并且子View的大小不能大于这个值。
- 01—–EXACTLY:精确模式,对应于
match_parent属性和具体的数值,父容器测量出 View所需要的大小,也就是SpecSize的值。
每一个普通View都有一个MeasureSpec属性来对其进行测量。而对于DecorView来说,它的MeasureSpec由自身的LayoutParams和窗口的尺寸决定。
View 根据传入的 MeasureSpec 约束和自身的 layout_width/layout_height 属性,计算出自己理想的尺寸。最后必须调用 setMeasuredDimension(int measuredWidth, int measuredHeight) 来保存最终确定的测量结果。
performMeasure 这个方法里,会对一众的 子ViewGroup 和 子View 进行测量。 View的onMeasure方法,实际是看 getDefaultSize() 来解析其宽高的,注意对于View基类来说,为了扩展性,它的两个MeasureSpec,AT_MOST和EXACTLY处理是一样的,即其宽高直接取决于所设置的specSize,所以自定义View直接继承于View的情况下,要想实现 wrap_content 属性,就需要重写onMeasure方法,自己设置一个默认宽高值。
ViewGroup的onMeasure
每个View的本身的onMeasure并不复杂,只需要关注好本身的尺寸就好了。
ViewGroup的Measure方法,它没有onMeasure,有一个 measureChildre() 方法:简单来说就是根据自身的 MeasureSpec和子元素的的 LayoutParams 属性来得出的子元素的 MeasureSpec 属性。它除了需要测量自己的宽与高之外,还需要逐个遍历子 view 以 measure 子 view。
如果 ViewGroup 自身是 EACTLY 的,那么 onMeasure 过程就会简单不少,因为它自身的宽与高是确定的,只需要挨个 measure 子View就可了,而且子View并不影响它本身。当然,要把 padding 和 margin 考虑进来。
最为复杂的就是 AT_MOST , ViewGroup 自身的宽与高是由其所有子View决定的,这才是最复杂的,也是各个ViewGroup子类布局器需要重点解决的,而且过程各不相同,因为每个布局器的特点不一样,所以过程并不相同,下面来各自讨论一下。
LinearLayout 的测量
它的方向只有两个,可以只分析一个方向,另外一个方向是差不多的,我们就看看垂直布局的measureVertical。
当height mode是EXACTLY的时候,这个时候LinearLayout布局本身的高度是已知的,挨个遍历子view然后measure一下就可以。
比较复杂的情况,是AT_MOST时,这其实也还好,理论上高度就是所有子view的高度之和。
最为复杂的情况是处理weight,这需要很多复杂处理,要把剩余所有的空间按weight来分配,具体比较复杂,有兴趣的可以具体去看源码。这也说明了,为何在线性布局中使用weight会影响性能,代码中就可以看出当有weight要处理的时候,至少多遍历一遍子view以进行相关的计算。
虽然方向是 VERTICAL 时,重点只处理垂直方向,但是width也是需要计算的,但width的处理就要简单得多,如果其是EXACTLY的,那么就已知了;如果是AT_MOST的,就要找子view中width的最大值。
FrameLayout 的测量
其实是最简单的一个布局管理器,因为它对子view是没有约束的,无论水平方向还是垂直方向,对子view都是没有约束,所以它的measure过程最简单。
如果是EXACTLY的,它本身的高度与宽度是确定的,那么就遍历子view,measure一下就可以了,最后再把margin和padding加一下就完事了。
如果是AT_MOST的,那么也不难,遍历子View并measure,然后取子view中最大宽为它的宽度,取最大的高为其高度,再加上margin和padding,基本上就做完了。
因为,FrameLayout的measure过程最为简单,因此系统里很多地方默认用的就是FrameLayout,比如窗口里的root view。
RelativeLayout
这个是最为复杂的,从设计的目的来看,RelativeLayout要解决的问题也是提供了长与宽两个维度来约束子view。
总体过的过程就是要 分别从vertical方向和horizontal方向,来进行两遍的measure ,同时还要计算具体的坐标,实际上RelativeLayout的measure过程是把measure和layout一起做了。
Layout 布局流程
onLayout() 这是 ViewGroup 需要重写的核心方法,对于 View 不需要重写 onLayout()。ViewGroup中的 layout() 方法用来确定子元素的位置,View中的 layout() 方法则用来确定自身的位置。所以是ViewGroup来计算子View的参数,并调用子控件的layout方法。
layout() 方法接收四个参数:左边界 (l)、上边界 (t)、右边界 (r)、下边界 (b)。这些坐标都是相对于 父 View 的坐标系 而言的。
View的layout方法,其中分别传入 left,top,right,bottom 四个参数,表示其距离父布局的四个距离,再走到setFrame,最后到onLayout,这是一个空方法,由继承的类自己实现。
LinearLayout
依然是两个方向,因为LinearLayout的目的就是在某一个方向上对子view进行约束。看layoutVertical就可以了,水平方向上逻辑是一样的。
遍历一次子View即可,从父布局的left, top起始,考虑子view的height 以及上下的padding和margin,依次排列就可以了。需要注意的是,对于left的处理,理论上子view的left就应该等于父布局,因为这毕竟是vertical的,水平上是没有约束的,但是也要考虑Gravity,当然也要把padding和margin考虑进来。最后通过setChildFrame把排列好的坐标设置给子view。
总体来看,线性布局的layout过程比其measure过程要简单不少。
FrameLayout
FrameLayout对子view的排列其实是没有约束的,所以layout过程也不复杂,遍历子view,子view的left和top初始均为父布局,依据其Gravity来做一下排布即可,比如如果Gravity是right,那么子view就要从父布局的右侧开始计算,childRight=parentRight-margin-padding,childLeft=childRight-childWidth,以次类推,还是比较好理解的。
RelativeLayout
前面提到过RelativeLayout是在measure的时候就把坐标都计算好了,它的layout就是把坐标设置给子view,其余啥也没有。
热门八股问题:为什么onResume获取不到View的宽高,而View.post就可以拿到?
- onCreate和onResume中无法获取View的宽高,是因为还没执行View的绘制流程。
- view.post之所以能够拿到宽高,是因为在绘制之前,会将获取宽高的任务放到Handler的消息队列,等到View的绘制结束之后,便会执行。
Draw 绘制流程
触发逻辑
从上面的讨论中可以看出draw的触发逻辑有两条路:
一是,没有启用硬件加速时,走的软件draw流程,也是一条比较好理解的简单流程: performTraversal->performDraw->draw->drawSoftware->View#draw 。
二是,启用了硬件加速时,走的是 performTraversal->performDraw->draw->ThreadedRenderer#draw ,到这里就走进了硬件加速相关的逻辑了。
硬件加速的绘制流程
遍历 DecorView 树,递归调用每个子View节点的 updateDisplayListIfDirty 函数,最终完成绘制树的创建。再通过JNI调用到Native层的RenderThread渲染线程,并唤醒渲染线程利用OpenGL执行渲染任务。
软件绘制
ViewRootImpl 是直接调用根节点的 draw() 方法,那么这里便是整个view tree的入口。可先从 View#draw(canvas) 方法看起。
主要分为四步:
- 画背景
drawBackground(); - 画自己的内容通过onDraw来委派,具体的内容是在onDraw里面做的;
- 画子view,通过
dispatchDraw()方法; - 画其他的东西,如scroll bar或者focus highlight等。
可以重点关注一下这些操作的顺序,先画背景,然后画自己,然后画子view,最后画scroll bar和focus之类的东西。
重点来看看dispatchDraw方法,因为其他几个都相对非常好理解,这个方法主要要靠ViewGroup来实现,因为在View里面它是空的,节点自己只需要管自己就可以了,只有父节点才需要关注如何画子View。
ViewGroup#dispatchDraw
这个方法做一些准备工作,如把 padding 考虑进来并进行clip,后会遍历子View,针对 每个子view调用 drawChild 方法,这实际上就 是调用回了 View#draw(canvas,parent,drawingTime) 方法,注意这个方法是package scope的,也就是说只能供view框架内部调用。这个方法并没有做具体的渲染工作(因为每个View的具体渲染都是在onDraw里面做的),这个方法里面做了大量与动画相关的各种变换。
Canvas对象是从哪里来的
View的渲染过程其实大都是 GUI框架内部的逻辑流程控制 ,真正涉及graphics方面的具体的图形如何画出来,其实都是由Canvas对象来做的,比如如何画点,如何画线,如何画文字,如何画图片等等。
一个Canvas对象从ViewRootImpl传给View tree,就在view tree中一层一层的传递,每个view都把其想要展示的内容渲染到Canvas对象中去。
那么,这个Canvas对象又是从何而来的呢?从view tree的一些方法中可以看到,都是从外面传进来的,view tree的各个方法(draw, dipsatchDraw和drawChild)都只接收Canvas对象,但并不创建它。
从上面的逻辑可以看到Canvas对象有二个来源:
- 一是在
ViewRootImpl中创建的,当走软件渲染时,会用Surface创建出一个Canvas对象,然后传给view tree。从ViewRootImpl的代码来看,它本身就会持有一个Surface对象,大概的逻辑就是每一个Window对象内,都会有一个用来渲染的Surface; - 另外一个来源就是走硬件加速时,会由
hwui创建出Canvas对象。
合成送显
这三大步走完之后,应用界面的内容用户依然还不可见,需要由 RenderThread 线程的渲染处理,渲染完成后,还需要通过Binder调用 “上帧” 交给 surfaceflinger 进程中进行合成后送显才能最终显示到屏幕上。
总结,应用在UI线程中从根节点DecorView出发,递归遍历每个子View节点,搜集其drawXXX绘制动作并转换成DisplayListOp命令,将其记录到DisplayListData并填充到RenderNode中,最终完成整个View绘制命令树的构建。从此UI线程的绘制任务就完成了。
C++层面的RenderThread 渲染流程
syncFrameState中遍历View树上每一个RenderNode,执行prepareTreeImpl函数,实现同步绘制命令树的操作;调用OpenGL库API使用GPU,按照构建好的绘制命令完成界面的渲染,将前面已经绘制渲染好的图形缓冲区Binder上帧给SurfaceFlinger合成和显示。
SurfaceFlinger作为系统中独立运行的一个Native进程,借用Android官网的描述,其为承上启下的角色,就是通过Surface与不同的应用进程建立联系,接收它们写入Surface中的绘制缓冲数据,对它们进行统一合成。然后对下层,通过屏幕的后缓存区与屏幕建立联系,发送合成好的数据到屏幕显示设备。
图形载体为Buffer,Surface为Buffer封装,管理了多个Buffer,内部是通过BufferQueue来管理的。这是一个生产者消费者模型, 应用进程为生产者,SurfaceFlinger为消费者 。应用进程开始界面渲染之前,通过Binder向 SurfaceFlinger 申请一张可用的buffer,使用CPU或者GPU渲染之后,将缓存数据返回给进程对应的 BufferQueue ,等其可用时申请sf类型的VSync信号,通知 SurfaceFlinger 去消费合成。 SurfaceFlinger 拿取buffer合成结束之后,再度将其置为free状态,返回对应 BufferQueue 中。