Framework学习之路(一)—— UI绘制深入源码分析
本篇为笔者对Android SDK 33版本的UI绘制入口进行追踪的过程,主要作笔记作用。由于笔者经验尚浅,水平也有限,所以会存在很多不足的地方。如果有大神发现问题,可以在评论区指出。
章节目录:
1、 ActivityThread的main方法、重要属性及其父类的介绍
1. ActivityThread及其main函数的介绍
4. ActivityThread的父类ClientTransactionHandler
(1) ActivityTaskManagerService
附录:ActivityTaskManagerService的启动
二、UI绘制流程
(2) PhoneWindow的重要属性——mDecor和mContentParent
(3) mDecor和mContentParent属性的初始化
附录一:常见的根布局——screen_simple.xml
2、AppCompatActivity加载XML文件的过程
1. 初始化PhoneWIndow的mDecor和mDecorContentParent属性
2. 初始化AppCompatDelegateImpl的mSubDecor属性
附录一:简单根布局abc_screen_simple.xml
3、UI绘制过程
附录一:将ViewRootImpl添加为DecorView的父布局
一、APP程序入口
1、ActivityThread的main方法、重要属性及其父类的介绍
1. ActivityThread及其main函数的介绍
/**
* ActivityThread是一个handeler类,这个看他的父类就可以知道
*/
public final class ActivityThread extends ClientTransactionHandler {
...
public static void main(String[] arg) {
//CloseGuard类实现了一种机制用于检查是否有内存泄露,,默认是关闭的,可以通过setEnabled(true)开启
CloseGuard.setEnabled(false);
//初始化Environment类,主要是一些目录的设置
//比如我们通常用的获取外部储存路径的函数Environment.getExternalStorageDirectory()的返回值就是在这里进行初始化的
Environment.initForCurrentUser();
//确保 TrustedCertificateStore 在正确的位置查找 CA 证书
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
//调用每个进程的主线模块初始化
initializeMainlineModules();
//初始化Handler中的Looper对象
Looper.prepareMainLooper();
...
//创建一个ActivityThread
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//初始化专门处理消息的handler对象
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始循环主线程里面的MessageQueue
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
...
}
2. ActivityThread重要属性的介绍
首先我们需要知道一点,ActivityThread不会持有Activity或者ActivityThread里面的任何东西,真正管理我们application或者activity的是AMS或者PMS。ActivityThread就是一个handler,它会不停的和我们的AMS或者PMS进行通信,然后得到结果,再来通过消息队列来执行它的handlerMessage。
我们接着看ActivityThread的几个主要的属性:
- mAppThread: ActivityThread不能直接和系统进行通讯,需要通过Binder,所以我们用ActivityTread的main方法不能直接获取Application(也就是不能直接启动应用),需要通过作为桥梁的ApplicationThread进行调用。而AMS作为桥梁的一端,会持有我们的ApplicationThread,然后来进行回调。(这里注意ApplicationThread是ActivityThread的子类)
- mLooper:ActivityThread作为一个handler想持续不停的往MessageQueue里面拿消息,肯定需要looper一直去轮询,也就是这里的mLooper属性。
- mInstrumentation: Instrumentation是所有的Application和Activity用来跟踪生命周期的类。ActivityThread不会直接的去执行某一个组件的生命周期方法,比如对于Application和Activity,它真正的启动是经过Instrumentation,Instrumentation是真正管理它们生命周期的类
public final class ActivityThread extends ClientTransactionHandler {
//ApplicationThread是ActivityThread与AMS通讯的桥梁,它作为服务端,接收ActivityManagerService的指令并执行
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
...
//真正管理Activity生命周期的类
Instrumentation mInstrumentation;
}
3. 启动主线程的Looper
//ActivityThread.java#main
Looper.prepareMainLooper(); //初始化Handler中的Looper对象
//Looper.java
...
private static Looper sMainLooper;
...
public static void prepareMainLooper() {
//设置当前线程为不可退出的线程
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//将主线程赋值给sMainLooper,这也是我们可以直接通过Looper.getMainLooper()获取主线程的原因
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
这里我们注意,main方法运行完就退出了,但是我们有个looper.loop,一直在循环的接受消息和发送消息,所以它会一直在运行,不会退出。
//ActivityThread.java#main
Looper.loop(); //开始循环主线程里面的MessageQueue
4. ActivityThread的父类ClientTransactionHandler
个人见解:ClientTransactionHandler既然是Handler,显然它会具备sendMessage方法,不过它并没有继承我们熟悉的Handler类,也就是说它虽然具备处理消息的职责,但真正处理消息的可能另有其人,比如ActivityThread就把真正处理消息的任务下发给了Handler类型的mH成员变量,这个后面会讲。
//ClientTransactionHandler.java
abstract void sendMessage(int what, Object obj);
我们看到ClientTransactionHandler里面有很多和Activity的生命周期有关的方法。
public abstract class ClientTransactionHandler {
...
public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason);
public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished,
boolean userLeaving, int configChanges, PendingTransactionActions pendingActions,
String reason);
public abstract void handleResumeActivity(@NonNull ActivityClientRecord r,
boolean finalStateRequest, boolean isForward, String reason);
public abstract void handleTopResumedActivityChanged(@NonNull ActivityClientRecord r,
boolean isTopResumedActivity, String reason);
public abstract void handleStopActivity(@NonNull ActivityClientRecord r, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
...
}
AMS通过ApplicationThread封装好数据,回调到ActivityThread,ActivityThread就可以通过发通知的方式,发给Handler,然后通过Handler的方法来执行相应的操作。
我们发现AMS->ApplicationThread->ActivityThread三者之间的分工非常明确,有点像MVP模式:
- AMS(M层):数据管理者。
- ApplicationThread(P层):通讯桥梁。
- ActivityThread(V层):具体逻辑的执行者。
这里有一点值得注意,Activity是在主线程中创建的,也就说ActivityThread的main方法所在的线程就是APP的主线程,这点在上面看到ActivityThread的Looper时就可见一斑。因为主线程是main方法所在的线程,也就是APP进程的第一个线程,所以主线程无法修改。
2、 Application的创建与启动
1. 创建ActivityThread实例
我们启动应用,就必须先创建Application。由于不确定项目中具体的Application类,所以不能通过new来创建,而是需要通过AMS去获取Application的类名信息,再通过反射将它实例化;若AMS获取不到,再使用new的方式创建默认的Application。
//ActivityThread.java#main
ActivityThread thread = new ActivityThread();//创建一个ActivityThread
thread.attach(false, startSeq);
2. 通过AMS获取Application信息
我们先看看attach方法做了什么?
//ActivityThread.java
private static volatile ActivityThread sCurrentActivityThread;
...
final ApplicationThread mAppThread = new ApplicationThread();
...
private void attach(boolean system, long startSeq) {
//ActivityThread赋值给自己的静态成员变量,是为了方便外部通过静态方法调用自己,算是单例模式的变种
sCurrentActivityThread = this;
...
//获取到ActivityManagerService的代理对象IActivityManager
final IActivityManager mgr = ActivityManager.getService();
try {
//调用AMS的attachApplication方法,mAppThread作为沟通桥梁传入该方法
mgr.attachApplication(mAppThread, startSeq); //PS:这里是AMS在attach方法里唯一调用地方
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}
我们看一下上面的IActivityManager的代理接口是如何获取的?
//ActivityThread.java#attach
final IActivityManager mgr = ActivityManager.getService();
//ActivityManager.java#getService
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
//通过binder机制进行进程通讯 从Activity系统服务中获取到AMS的代理对象
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
我们可以看到IActivityManager从系统服务管理者ServiceManager中获取到了可以和系统服务进程的AMS进行远程通讯Binder通道。
接下来看看ActivityThread的attach方法,在获取AMS的代理对象后,做了什么事情?
//ActivityThread.java#attach
mgr.attachApplication(mAppThread, startSeq);
//ActivityManagerService.java#attachApplication
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
if (thread == null) {
throw new SecurityException("Invalid application interface");
}
synchronized (this) {
//从Binder中获取到一些进程信息
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq); //* 重点方法
Binder.restoreCallingIdentity(origId);
}
}
//ActivityManagerService.java#attachApplicationLocked
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
///(1)
//查找正在附加的应用程序记录...
//如果我们在多个进程中运行,则通过PID,或者如果我们使用匿名线程模拟进程,则只需拉取下一个应用程序记录。
ProcessRecord app; //(2) 储存进程信息,attachApplicationLocked方法里面存在大量对该对象及其属性进行赋值的代码
...
//(3)
if (app.getIsolatedEntryPoint() != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(
app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs());
} else if (instr2 != null) {
thread.bindApplication(processName, appInfo,
app.sdkSandboxClientAppVolumeUuid,
app.sdkSandboxClientAppPackage,
...);
}else {
thread.bindApplication(processName, appInfo,
app.sdkSandboxClientAppVolumeUuid,
app.sdkSandboxClientAppPackage,
...);
}
...
}
//ActivityThread$ApplicationThread.java#bindApplication
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,
...) {
...
AppBindData data = new AppBindData(); //(4)
data.processName = processName;
data.appInfo = appInfo;
...
sendMessage(H.BIND_APPLICATION, data); //(5)
}
//ActivityThread.java#sendMessage
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) {
Slog.v(TAG,
"SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj);
}
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg); //(6)
}
//ActivityThread$H.java#handleMessage
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data); //(7)
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
...
}
① 在创建Application之前,要从AMS中获取Application的一些比较重要的信息。比如我们的Application到底是自己创建的,还是别人传过来的?还是系统原生的,或者系统自带的,或者系统自己创建的?我们都要进行判断,而这些信息都保留在AMS里面。
② ProcessRecord是进程记录者,该类专门储存当前进程的所有信息,这就说明它也保存了Application的一些信息。(该类存在很多成员变量,如果我们看到类似的成员变量多的类,就可以把它认为是专门储存数据的类)
③ AMS将数据封装到ProcessRecord等变量之后,通过传入大量参数的形式将信息回调给IApplicationThread。
④ ActivityThread.AppBindData是应用数据的封装类,ApplicationThread把AMS传过来的数据全部都传给了AppBindData,也就是说AppBindData携带了Application的信息。
⑤ 将AppBindData传递给ActivityThread的sendMessage方法。(因为ApplicationThread并不是ActivityThread静态内部类,所以可以直接调用ActivityThread的方法)
⑥ ActivityThread里面所有的通知最终都是在mH里面进行了具体的操作,这个上面有提到。
⑦ handleBindApplication方法对Application进行了绑定。
本小节后续内容,都会在handleBindApplication方法里面展开,我们会看到ActivityThread在handleBindApplication方法里面依次创建了Instrumentation、Application,然后调用了Application的onCreate方法。
3. 创建Instrumentation实例
//ActivityThread.java#handleBindApplication
private void handleBindApplication(AppBindData data) {
...
//mInstrumentation在这里进行了初始化
if (ii != null) {
initInstrumentation(ii, data, appContext); //* 重点方法
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
}
...
}
//ActivityThread.java#initInstrumentation
private void initInstrumentation(
InstrumentationInfo ii, AppBindData data, ContextImpl appContext) {
...
try {
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance(); //通过反射的方式来创建mInstrumentation的实例
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
...
}
4. 创建Application实例
initInstrumentation方法执行结束后,我们得到了mInstrumentation实例,接着我们重新回到ActivityThread的handleBindApplication方法这里,看看Application是如何创建的。
//ActivityThread.java#handleBindApplication
private void handleBindApplication(AppBindData data) {
...
//mInstrumentation初始化完成后开始创建Application对象
Application app;
...
app = data.info.makeApplicationInner(data.restrictedBackupMode, null); //通过LoadedApk创建application
...
}
//LoadedApk.java#makeApplicationInner
public final class LoadedApk {
...
//application缓存的实例
private Application mApplication;
...
//我们在此处缓存此进程中每个包的实例化应用程序对象。
private static final ArrayMap<String, Application> sApplications = new ArrayMap<>(4);
...
public Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
return makeApplicationInner(forceDefaultAppClass, instrumentation,
/* allowDuplicateInstances= */ false);
}
private Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation, boolean allowDuplicateInstances) {
if (mApplication != null) {
//如果application已经存在,则直接返回
return mApplication;
}
...
//从缓存的sApplications集合中尝试获取Application
synchronized (sApplications) {
final Application cached = sApplications.get(mPackageName);
if (cached != null) {
...
if (!allowDuplicateInstances) {
mApplication = cached;
return cached;
}
}
}
//创建Application对象
Application app = null;
...
//获取application类名
String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(myProcessName);
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
...
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext); //(1)
...
//缓存Application
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (!allowDuplicateInstances) {
synchronized (sApplications) {
sApplications.put(mPackageName, app);
}
}
}
}
//Instrumentation.java#newApplication
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
app.attach(context); //application创建后,调用了attach方法,而attach方法里面调用了我们熟知的attachBaseContext方法。
return app;
}
//AppComponentFactory.java#instantiateApplication
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Application) cl.loadClass(className).newInstance(); //(2) 这里就是通过类加载器最终创建了我们的Application
}
① ActivityThread通过的mInstrumentation来创建Application实例。我们之前提过,Application和Activity包括创建和生命周期等操作都是由mInstrumentation来监管的。
② Instrumentation最终通过AppComponentFactory的instantiateApplication方法,使用类加载器创建了Application。
5. 启动Application
我们继续看handleBindApplication方法,在mInstrumentation和app都创建完成后,我们继续跟踪mInstrumentation,就可以看到它使用callApplicationOnCreate方法调用了app的onCreate方法,mInstrumentation监听Application和Activity其实都是通过这样的方式。
调用了onCreate方法后,Application就已经启动了。
//ActivityThread.java#handleBindApplication
private void handleBindApplication(AppBindData data) {
...
mInstrumentation.callApplicationOnCreate(app);
...
}
//Instrumentation.java#callApplicationOnCreate
public void callApplicationOnCreate(Application app) {
app.onCreate();
}
3、Activity的创建和启动
1. 系统进程的准备工作
(1) ActivityTaskManagerService
前面我们在ActivityManagerService的attachApplicationLocked方法中创建了Application,创建完成后,也会在该方法中创建Activity。
个人见解:attachApplicationLocked方法中必然创建了Actiivity,我们回顾上面的ActivityThread的main方法调用AMS的代码,发现只有attach方法沟通了AMS,追踪后可以发现attach方法调用的AMS方法中,核心逻辑都在attachApplicationLocked方法里面,也就是说在应用启动过程中,attachApplicationLocked方法就是AMS的核心逻辑,这也就意味着启动Application和MainActivity的逻辑也必然在此方法中。
所以我们就attachApplicationLocked方法往下跟踪,发现application启动后,执行了下面的代码。
//ActivityManagerService.java#attachApplicationLocked
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
// 查看此进程中最可见的活动是否正在等待运行...
if (normalMode) {
try {
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); // (1)
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
//查找应在此进程中运行的任何服务...
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName); // (2)
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
}
①Activity创建与启动的逻辑就在mAtmInternal的attachApplicationLocked方法中。
② mServices是ActiveServices,其attachApplicationLocked方法创建的是Service而不是Activity,这里不要搞混。
那么我们来看看mAtmInternal究竟是谁?
//ActivityManagerService.java
public ActivityTaskManagerInternal mAtmInternal;
//ActivityTaskManagerInternal.java
public abstract class ActivityTaskManagerInternal {
...
}
//ActivityManagerService.java#Constructor
public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
...
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); //这里创建并启动了Activity
...
}
//LocalServices
public final class LocalServices {
...
private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
new ArrayMap<Class<?>, Object>();
/**
* 返回实现指定接口的本地服务实例。
* 参数:类型 – 服务的类型。
* 返回:服务对象。
*/
public static <T> T getService(Class<T> type) {
synchronized (sLocalServiceObjects) {
return (T) sLocalServiceObjects.get(type);
}
}
/**
* 将指定接口的服务实例添加到本地服务的全局注册表中。
*/
public static <T> void addService(Class<T> type, T service) {
synchronized (sLocalServiceObjects) {
if (sLocalServiceObjects.containsKey(type)) {
throw new IllegalStateException("Overriding service registration");
}
sLocalServiceObjects.put(type, service);
}
}
...
}
mAtmInternal是AMS的成员变量,是ActivityTaskManagerInternal抽象类型的实例对象。从AMS的构造方法中,可以发现mAtmInternal是来自LocalServices缓存的某个服务。
这时候想要知道mAtmInternal究竟是什么类型的对象,我们就需要知道是谁把它存到了LocalServices中,但这并不好找。
干脆以“ActivityTaskManager”这个字段来搜索,这时候就发现了一个可疑的类型:ActivityTaskManagerService(ActivityTaskManagerService的启动见本节附录)。
搜索一下“ActivityTaskManagerInternal.class”,果然找到了mAtmInternal的入口。
//ActivityTaskManagerService.java
...
final ActivityTaskManagerInternal mInternal;
...
public ActivityTaskManagerService(Context context) {
...
mInternal = new LocalService();
...
}
...
private void start() {
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal); //ActivityManagerService#mAtmInternal的来源
}
我们发现mInternal是LocalService类型的对象(注意这个LocalService并不是缓存服务的LocalServices),而LocalService是ActivityTaskManagerService的内部类。
//ActivityTaskManagerService$LocalService.java
final class LocalService extends ActivityTaskManagerInternal {
...
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLockWithoutBoost) {
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
}
try {
return mRootWindowContainer.attachApplication(wpc); //* 核心方法
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
...
}
(2) WindowContainer
//ActivityTaskManagerService$LocalService.java#attachApplication
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
...
return mRootWindowContainer.attachApplication(wpc);
...
}
//ActivityTaskManagerService.java
RootWindowContainer mRootWindowContainer;
RootWindowContainer是窗口容器(WindowContainer)的根容器,管理所有的窗口容器,设备上所有的窗口(Window)、显示屏幕(Display)都是由它来管理的。
//RootWindowContainer.java
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
...
private final AttachApplicationHelper mAttachApplicationHelper = new AttachApplicationHelper();
...
boolean attachApplication(WindowProcessController app) throws RemoteException {
try {
return mAttachApplicationHelper.process(app);
} finally {
mAttachApplicationHelper.reset();
}
}
private class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {
...
private ActivityRecord mTop;
...
boolean process(WindowProcessController app) throws RemoteException {
mApp = app;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
getChildAt(displayNdx).forAllRootTasks(this); //(1)
if (mRemoteException != null) {
throw mRemoteException;
}
}
if (!mHasActivityStarted) {
ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */);
}
return mHasActivityStarted;
}
@Override
public void accept(Task rootTask) {
if (mRemoteException != null) {
return;
}
if (rootTask.getVisibility(null /* starting */)
== TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
return;
}
mTop = rootTask.topRunningActivity(); //(2)
rootTask.forAllActivities(this);
}
...
}
}
① 遍历RootWindowContainer所有的窗口和显示屏幕,这里最终会调用AttachApplicationHelper的accept方法
② Task用来描述一个Activity任务栈,而ActivityRecord记录了Activity的所有信息。这里是通过调用rootTask的topRunningActivity方法获取到栈顶Activity的信息,然后存到了mTop。(这里栈顶Activity并不是我们熟知的Activity,而是记录Activity配置信息的ActivityRecord)
//Task.java#topRunningActivity
class Task extends TaskFragment {
ActivityRecord topRunningActivity(IBinder token, int taskId) {
final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTopRunning,
PooledLambda.__(ActivityRecord.class), taskId, token);
final ActivityRecord r = getActivity(p);
p.recycle();
return r;
}
}
//TaskFragment.java
class TaskFragment extends WindowContainer<WindowContainer> {
...
}
//WindowContainer.java#getActivity
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
InsetsControlTarget {
ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
ActivityRecord boundary) {
...
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
...
final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary);
...
}
...
}
}
Task的topRunningActivity方法最终调用了WindowContainer的getActivity方法,该方法遍历Task下所有的窗口。
得到了ActivityRecord后,接着调回了RootWindowContainer$AttachApplicationHelper.java的test方法,尝试创建并启动Activity。
(PS:accept方法和test方法的具体调用方还没仔细找,这里只是猜测)
//RootWindowContainer$AttachApplicationHelper.java
private class AttachApplicationHelper implements Consumer<Task>, Predicate<ActivityRecord> {
boolean process(WindowProcessController app) throws RemoteException {
...
if (!mHasActivityStarted) { //(3)
ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */); //(4)
}
return mHasActivityStarted;
}
...
@Override
public boolean test(ActivityRecord r) {
if (r.finishing || !r.showToCurrentUser() || !r.visibleIgnoringKeyguard
|| r.app != null || mApp.mUid != r.info.applicationInfo.uid
|| !mApp.mName.equals(r.processName)) {
return false;
}
try {
if (mTaskSupervisor.realStartActivityLocked(r, mApp,
mTop == r && r.getTask().canBeResumed(r) /* andResume */,
true /* checkConfig */)) { //(1)
mHasActivityStarted = true; //(2)
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity " + mTop, e);
mRemoteException = e;
return true;
}
return false;
}
}
通过调用mTaskSupervisor的realStartActivityLocked方法(见①),真正创建并启动了Activity,如果启动成功设置mHasActivityStarted为true(见②),回到process方法后会判断Activity是否启动成功(见③),如果失败,会调用ensureActivitiesVisible方法确保活动可见(见④)。
我们看看mTaskSupervisor是如何创建Activity的?
(3) ClientTransaction
//RootWindowContainer.java#mTaskSupervisor
ActivityTaskSupervisor mTaskSupervisor;
//ActivityTaskSupervisor.java#realStartActivityLocked
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
...
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
...
// 创建活动启动事务。
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.token); //(1) 这里的proc.getThread()其实就是ApplicationtThread
final boolean isTransitionForward = r.isTransitionForward();
final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
//* 添加LaunchActivityItem回调对象,这个后面会提到
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), ...);
...
//提交事务
mService.getLifecycleManager().scheduleTransaction(clientTransaction); //(3) 将事务提交给了ClientLifecycleManager
}
...
}
//ClientLifecycleManager.java#scheduleTransaction
class ClientLifecycleManager {
...
/**
* 调度一个事务,该事务可能由多个回调和一个生命周期请求组成
*/
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
transaction.schedule(); //* 核心方法
if (!(client instanceof Binder)) {
// If client is not an instance of Binder - it's a remote call and at this point it is
// safe to recycle the object. All objects used for local calls will be recycled after
// the transaction is executed on client in ActivityThread.
transaction.recycle();
}
}
...
}
//ClientTransaction.java#schedule
public class ClientTransaction implements Parcelable, ObjectPoolItem {
...
private IApplicationThread mClient;
...
public void schedule() throws RemoteException {
mClient.scheduleTransaction(this); //(4)
}
...
}
① 这里创建了ClinetTransaction, ActivityThread创建Activity需要一个用来启动Activity事物的对象,也就是ClinetTransaction,ClinetTransaction就是用来启动我们的Activity的。(ClientTransaction的obtain方法传入了ApplicationtThread,说明Activity在该obtain方法里面完成创建)
② 这里添加了回调,并把LaunchActivityItem传到了ClientTransaction内。
③ 将Activity的启动事务提交给了ClientLifecycleManager。
④ 这里调用了ApplicationThread代理对象的scheduleTransaction方法,也就是从系统进程回到了APP进程。
我们来看看ApplicationThread是如何传入ClientTransaction的。
//ActivityTaskSupervisor.java#realStartActivityLocked
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
...
final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.token);
...
}
//ClientTransaction.java#obtain
public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
if (instance == null) {
instance = new ClientTransaction();
}
instance.mClient = client;
instance.mActivityToken = activityToken;
return instance;
}
附录:ActivityTaskManagerService的启动
可以在SystemService中看到ActivityTaskManagerService的启动,这个了解一下就可以了。
//SystemServer.java
...
public static void main(String[] args) {new SystemServer().run();}
...
private void run() {
...
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
startApexServices(t);
...
}
...
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
ActivityTaskManagerService atm = mSystemServiceManager.startService(
ActivityTaskManagerService.Lifecycle.class).getService();
mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
mWindowManagerGlobalLock = atm.getGlobalLock();
...
}
2. 创建Activity实例
//ActivityThread$ApplicationThread.java#scheduleTransaction
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
//ActivityThread.java
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
...
}
//ClientTransactionHandler.java
public abstract class ClientTransactionHandler {
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
}
//ActivityThread$H.java#handleMessage
public final class ActivityThread extends ClientTransactionHandler
implements ActivityThreadInternal {
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this); //*
...
class H extends Handler {
public void handleMessage(Message msg) {
...
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction); //mTransactionExecutor是执行事务的对象
if (isSystem()) {
transaction.recycle();
}
break;
...
}
}
}
//TransactionExecutor.java#execute
public void execute(ClientTransaction transaction) {
...
executeCallbacks(transaction);
executeLifecycleState(transaction);
...
}
//TransactionExecutor.java#executeCallbacks
private ClientTransactionHandler mTransactionHandler; //(1)
...
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); //获取transaction的所有回调类
...
//遍历事务管理器中的所有窗口请求对象
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i); //*
...
item.execute(mTransactionHandler, token, mPendingActions); //(2) 进行窗体创建请求
item.postExecute(mTransactionHandler, token, mPendingActions);
}
...
}
//ClientTransactionItem.java
public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
}
② item的execute方法有个mTransactionHandler参数是ClientTransactionHandler类型①的成员变量,而ClientTransactionHandler则是ActivityThread的父类,也就是说这里传入execute的是ActivityThread。
在TransactionExecutor.java#executeCallbacks方法中,遍历了ClientTransaction所有的回调对象,也就是ClientTransactionItem的实例,然后调用了它们的execute方法,来进行窗口创建请求。
而这些ClientTransactionItem实例是在哪里添加进来的?之前在3-1-(3)-ActivityTaskSupervisor.java#realStartActivityLocked方法中有提到,添加进来的就是LaunchActivityItem,而LaunchActivityItem是ClientTransactionItem的实现类。
//LaunchActivityItem.java#execute
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
//创建一个ActivityClientRecord对象,用于Activity的实例化
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
mTaskFragmentToken);
//将创建的ActivityClientRecord回调给ActivityThread
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
//ActivityThread.java#handleLaunchActivity
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
//根据传过来的ActivityClientRecord创建一个Activity
final Activity a = performLaunchActivity(r, customIntent);
...
}
//ActivityThread.java#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); //创建Activity
...
}
//Instrumentation.java#newActivity
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//通过意图获取目标Activity的包名,用来获取Activity工厂
String pkg = intent != null && intent.getComponent() != null
? intent.getComponent().getPackageName() : null;
这里获取AppComponentFactory实例后,创建了Activity
return getFactory(pkg).instantiateActivity(cl, className, intent); //(1)
}
//AppComponentFactory.java
public class AppComponentFactory {
...
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity) cl.loadClass(className).newInstance(); //类加载器创建Activfity实例
}
...
}
① 详情见本节附录
Activity的启动会通过Intent拿到目的地Activity的全类名,然后根据类加载器来进行实例化。
附录:AppComponentFactory
public class AppComponentFactory {
public @NonNull ClassLoader instantiateClassLoader(@NonNull ClassLoader cl,
@NonNull ApplicationInfo aInfo) {...}
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,@NonNull String className) { ...}
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent) {...}
public @NonNull BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent) {...}
public @NonNull Service instantiateService(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent){...}
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className) {,,,}
public static final AppComponentFactory DEFAULT = new AppComponentFactory();
}
可以看到所有的组件都是通过AppComponentFactory创建的。
接下来看看Instrumentation是怎么获取AppComponentFactory的。
//Instrumentation.java#getFactory
private ActivityThread mThread = null;
...
private AppComponentFactory getFactory(String pkg) {
if (pkg == null) { //如果没有Intent携带的全类名,就用默认工厂对象
Log.e(TAG, "No pkg specified, disabling AppComponentFactory");
return AppComponentFactory.DEFAULT;
}
if (mThread == null) { //如果没有ActivityThread,也用默认工厂对象
Log.e(TAG, "Uninitialized ActivityThread, likely app-created Instrumentation,"
+ " disabling AppComponentFactory", new Throwable());
return AppComponentFactory.DEFAULT;
}
LoadedApk apk = mThread.peekPackageInfo(pkg, true); //根据包名获取当前apk对应的LoadedApk对象
// This is in the case of starting up "android".
if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
//获取由manifest文件<application>标签的android:appComponentFactory属性指定的工厂,也就是可以自定义应用组件实例化的工厂
return apk.getAppFactory();
}
//ActivityThread.java#peekPackageInfo
//这里缓存了不同APK对应的LoadedApk对象,也就是可以通过这些map来实现APP间的远程通讯
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
...
public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
//ActivityThead通过弱引用的方式缓存不同APK的LoadedApk对象,这里根据APK的包名取出对应的LoaderApk
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
ref = mPackages.get(packageName);
} else {
ref = mResourcePackages.get(packageName);
}
return ref != null ? ref.get() : null;
}
}
//LoadedApk.java#createAppFactory
private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
if (mIncludeCode && appInfo.appComponentFactory != null && cl != null) {
try {
//如果清单文件指定了appComponentFactory就通过类加载器创建该对象,否则返回默认的AppComponentFactory对象
return (AppComponentFactory)
cl.loadClass(appInfo.appComponentFactory).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
Slog.e(TAG, "Unable to instantiate appComponentFactory", e);
}
}
return AppComponentFactory.DEFAULT;
}
3. 启动Activity
现在Activity已经创建,我们回到创建Activity的创建的地方,看看Activity创建后是如何启动的。
//ActivityThread.java#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
...
activity = mInstrumentation.newAct\ivity(cl, component.getClassName(), r.intent); //创建Activity
...
activity.attach(appContext, **); //调用Activity的attachBaseContext方法
...
//调用Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
}
//Instrumentation.java#callActivityOnCreate
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
//Activity.java#performCreate
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
...
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
...
}
Activity启动完成后,接下来就是启动UI页面了。
二、UI绘制流程
1、Activity加载XML文件的过程
1. PhoneWindow的初始化
(1) 创建PhoneWindow
上一小节,我们已经追踪到了Activity的onCreate方法,这UI绘制流程的入口呼之欲出,也就是setContentView方法了。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
不妨让我们看看setContentView做了什么。
//Activity.java#setContentView
public void setContentView(@LayoutRes int layoutResID) {
//实际上调用的是PhoneWindow.setContentView()方法 --> PhoneWindow是window的唯一实现类
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
//Activity.java#getWindow
private Window mWindow;
...
public Window getWindow() {
return mWindow;
}
//Window.java#setContentView
public abstract class Window {
...
public abstract void setContentView(View view);
...
}
//Activity.java#mWindow
final void attach(Context context, ...) {
attachBaseContext(context);
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
可以看到mWindow其实是PhoneWindow(该类其实也是Window的唯一实现类)对象,也就是Activity#setContentView,后面调用的其实是PhoneWindow#setContentView。
番外话:从Activity#attach可以看出,执行attachBaseContext方法时,mWIndow还未实例化,所以attachBaseContext中不能执行setContentView方法
(2) PhoneWindow的重要属性——mDecor和mContentParent
在追踪PhoneWindow#setContentView之前,先来了解一下PhoneWindow的几个重要的属性。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
//这是窗口的顶级视图,它可以包含所有的窗口修饰
private DecorView mDecor; //(1)
/**
* 布局容器
* 这是放置窗口内容的视图。它要么是mDecor本身,要么是mDecor的内容所在的子级。
*/
ViewGroup mContentParent; //(2)
}
① DecorView是顶层的视图,是Activity最外层的View。
② mContentParent要么是mDecor,要么是mDecor的子View。
(3) mDecor和mContentParent属性的初始化
接下来看看PhoneWindow#setContentView做了什么?
//PhoneWindow.java#setContentView
public void setContentView(int layoutResID) {
//mContentParent肯定会为空 因为我们当前还是窗体初始化
if (mContentParent == null) {
//初始化顶层布局
installDecor(); //这里做了两件事,分别是mDecor的初始化和mContentParent的初始化
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
...
}
//PhoneWindow.java#installDecor
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//初始化mDecor
mDecor = generateDecor(-1); //创建mDecor
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//通过mDecor初始化ContentParent
mContentParent = generateLayout(mDecor);
...
}
}
[1] 初始化mDecor属性
先看一下mDecor初始化时,PhoneWindow.java#generateDecor方法做了什么。
//PhoneWindow.java#generateDecor
protected DecorView generateDecor(int featureId) {
//系统进程没有应用程序上下文,在这种情况下,我们需要直接使用我们拥有的上下文。否则,我们需要应用程序上下文,因此我们不会紧紧抓住活动。
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, this);
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes()); //创建DecorView对象
}
PhoneWindow.java#generateDecor只是简单new了一个DecorView对象。
[2] 初始化mContentParent属性
接着看ContentParent初始化时,PhoneWindow.java#generateLayout方法做了什么。
//PhoneWindow.java#generateLayout
protected ViewGroup generateLayout(DecorView decor) {
//获取应用当前主题中的数据,比如是否全屏、悬浮、有无title等
TypedArray a = getWindowStyle();
...
//获取窗体布局属性
WindowManager.LayoutParams params = getAttributes();
...
//给窗体进行装饰
int layoutResource; //资源ID,他会根据不同的主题加载对应的XML文件
int features = getLocalFeatures();
//加载系统布局 判断到底是加载哪个系统的布局
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
} ... else {
layoutResource = R.layout.screen_simple; //(1)
}
mDecor.startChanging();
//将加载到的基础布局添加到了最外层View,也就是mDecor里面。
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //(2)
//通过系统content资源ID去加载实例化这个控件
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //(3)
if (contentParent == null) {
//如果contentParent为null,就会抛出异常。这点毋庸置疑,它肯定不能为空
throw new RuntimeException("Window couldn't find content container view");
}
...
return contentParent; //这里返回了ContentView
}
//Window.java#ID_ANDROID_CONTENT
/**
* XML布局文件中主布局应具有的ID
*/
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; //(4)
① R.layout.screen_simple是最常见的布局,详见本节附录一。
② DecorView通过onResourcesLoaded方法添加layoutResource基础布局,详见本节附录二。
③④ R.id.content,就是附录一中根布局的FrameLayout的id,也就是说③这里拿到了ContentView,并赋值给contentParent局部变量。
我们回到PhoneWindow,在2-1-1-(2)中,我们提到过mContentParent是mDecor,要么是mDecor的子View。通过上面的跟踪,发现我们现在得到的mContentParent正是根布局的ContentView,也就是mDecor的子View。
//PhoneWindow.java#installDecor
ViewGroup mContentParent;
...
private void installDecor() {
if (mContentParent == null) {
//通过mDecor初始化ContentParent
mContentParent = generateLayout(mDecor);
...
}
}
这时候PhoneWindow.java#installDecor方法的两个初始化操作已经完成了,mDecor和mContentPare都完成了初始化。
附录一:常见的根布局——screen_simple.xml
下面就是最常见的根布局XML文件的代码。
<!--screen_simple.xml-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
R.id.action_mode_bar_stub待会再提。
R.id.content是状态栏下的页面,也就是手机显示屏上的主体内容。它是Activity#setContentView对应的ContentView,一般情况下,我们自己写的布局文件真正的父布局就是ContentView。
action_mode_bar_stub和content分别对应图片2-1-1-2中的TitleView和ContentView,不过还是有些出入,所以笔者又找了一张更合适的图片2-1-1-a。
![](https://img-blog.csdnimg.cn/7f8ac5e4b5234ea1844c48a87de7103d.webp#pic_center)
接下来特别说说R.id.action_mode_bar_stub,之所以特别说,是因为我在追踪R.id.action_mode_bar_stub的时候绕了个弯路,下面讲讲踩坑过程。
R.id.action_mode_bar_stub之前以为是状态栏,直到追踪AppCompatActivity的时候(详见2-2),发现有两个R.id.action_mode_bar_stub,难道AppCompatActivity还能有两个状态栏不成???
绝对不可能!!!
于是按惯性思维,认为AppCompatActivity的R.id.action_mode_bar_stub替换了原来的R.id.action_mode_bar_stub,这样的话它替换的逻辑应该类似ContentView,那么它必然是在布局初始化的时候发生的,思路有了之后,以PhoneWindow.java#generateLayout和AppCompatDelegateImpl.java#ensureSubDecor方法为核心追踪了半天,结果当然是追踪了个寂寞。
干脆用"R.id.action_mode_bar_stub"来进行查找,很快我在DecorView找到了它。
//DecorView.java#createStandaloneActionMode
private ActionBarContextView mPrimaryActionModeView;
...
private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
...
ViewStub stub = findViewById(R.id.action_mode_bar_stub);
if (stub != null) {
mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
mPrimaryActionModePopup = null;
}
...
}
ActionBarContextView?这是和标题栏有关?再看一下调用的方法名createStandaloneActionMode,创建ActionMode?
于是我有了一种不祥的预感,反向追踪一下代码。
//DecorView.java#createStandaloneActionMode
private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
...
}
//DecorView.java#createActionMode
private ActionMode createActionMode(
....
return createStandaloneActionMode(callback);
...
}
//DecorView.java#startActionMode
private ActionMode startActionMode(
View originatingView, ActionMode.Callback callback, int type) {
...
mode = createActionMode(type, wrappedCallback, originatingView);
...
}
可以看到在启动ActionMode的时候,膨胀了action_mode_bar_stub。
再回忆一下id的名字action_mode_bar_stub和它的类型ViewStub(状态栏还能不可见?)。
这时候该抽自己一巴掌,action_mode_bar_stub和状态栏不能说一模一样,只能说毫无关系。
那么状态栏究竟是谁?
这时候为了避免干扰,设置一下无标题主题,然后运行了一下程序。
打开SDK->tools->bin->uiautomatorviewer.bat,终于看到了正正经经的状态栏R.id.statusBarBackground(如图2-1-1-a2)。
为了更清晰的和action_mode_bar_stub进行对比,又打开了SDK->tools->monitor.bat->HierarchyView(如同2-1-1-a3)。
![]() 2-1-1-a2 | ![]() 2-1-1-a3 |
在DecorView中可以找到ColorViewState类型的mStatusColorViewState变量,它持有状态栏,也持有状态栏的状态和资源ID等属性。
//DecorView.java#mStatusColorViewState
public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
new ColorViewAttributes(FLAG_TRANSLUCENT_STATUS,
Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.statusBarBackground, ITYPE_STATUS_BAR);
...
private final ColorViewState mStatusColorViewState =
new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
//DecorView$ColorViewState.java
private static class ColorViewState {
View view = null;
int targetVisibility = View.INVISIBLE;
boolean present = false;
boolean visible;
int color;
final ColorViewAttributes attributes;
ColorViewState(ColorViewAttributes attributes) {
this.attributes = attributes;
}
}
//DecorView$ColorViewAttributes.java
public static class ColorViewAttributes {
final int id;
final int translucentFlag;
final int verticalGravity;
final int horizontalGravity;
final int seascapeGravity;
final String transitionName;
final @InternalInsetsType int insetsType;
private ColorViewAttributes(int translucentFlag, int verticalGravity, int horizontalGravity,
int seascapeGravity, String transitionName, int id,
@InternalInsetsType int insetsType) {
this.id = id;
this.translucentFlag = translucentFlag;
this.verticalGravity = verticalGravity;
this.horizontalGravity = horizontalGravity;
this.seascapeGravity = seascapeGravity;
this.transitionName = transitionName;
this.insetsType = insetsType;
}
public boolean isPresent(boolean requestedVisible, int windowFlags, boolean force) {
return requestedVisible
&& ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || force);
}
public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
return present
&& (color & Color.BLACK) != 0
&& ((windowFlags & translucentFlag) == 0 || force);
}
public boolean isVisible(InsetsState state, int color, int windowFlags, boolean force) {
final boolean present = isPresent(state.getSource(insetsType).isVisible(), windowFlags,
force);
return isVisible(present, color, windowFlags, force);
}
}
接下来看看状态栏的初始化过程。
//ActivityThread.java#handleStartActivity
public void handleStartActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
...
mInstrumentation.callActivityOnPostCreate(activity, ...);
...
}
//Instrumentation.java#callActivityOnPostCreate
public void callActivityOnPostCreate(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
activity.onPostCreate(savedInstanceState);
}
//Activity.java#onPostCreate
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
if (!isChild()) {
mTitleReady = true;
onTitleChanged(getTitle(), getTitleColor());
}
mCalled = true;
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
}
//Activity.java#setTitle
protected void onTitleChanged(CharSequence title, int color) {
if (mTitleReady) {
final Window win = getWindow();
if (win != null) {
win.setTitle(title);
if (color != 0) {
win.setTitleColor(color);
}
}
if (mActionBar != null) {
mActionBar.setWindowTitle(title);
}
}
}
//PhoneWindow.java#setTitle
public void setTitle(CharSequence title) {
setTitle(title, true);
}
public void setTitle(CharSequence title, boolean updateAccessibilityTitle) {
if (mTitleView != null) {
mTitleView.setText(title);
} else if (mDecorContentParent != null) {
mDecorContentParent.setWindowTitle(title);
}
mTitle = title;
if (updateAccessibilityTitle) {
WindowManager.LayoutParams params = getAttributes();
if (!TextUtils.equals(title, params.accessibilityTitle)) {
params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
if (mDecor != null) {
// ViewRootImpl will make sure the change propagates to WindowManagerService
ViewRootImpl vr = mDecor.getViewRootImpl();
if (vr != null) {
vr.onWindowTitleChanged();
}
}
dispatchWindowAttributesChanged(getAttributes());
}
}
}
//PhoneWindow.java#dispatchWindowAttributesChanged
protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) {
super.dispatchWindowAttributesChanged(attrs);
if (mDecor != null) {
mDecor.updateColorViews(null /* insets */, true /* animate */);
}
}
//DecorView.java#updateColorViews
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
...
updateColorViewInt(mStatusColorViewState, statusBarColor, 0,
mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
statusBarSideInset, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, controller);
...
}
//DecorView.java#updateColorViewInt
private void updateColorViewInt(final ColorViewState state, int color, int dividerColor,
int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate,
boolean force, WindowInsetsController controller) {
...
if (view == null) {
if (showView) {
state.view = view = new View(mContext); //初始化状态栏对应的View
setColor(view, color, dividerColor, verticalBar, seascape);
view.setTransitionName(state.attributes.transitionName);
view.setId(state.attributes.id); //状态栏的ID
visibilityChanged = true;
view.setVisibility(INVISIBLE);
state.targetVisibility = VISIBLE;
...
addView(view, lp); //将状态栏添加到DecorView中
...
}
}
}
到这里状态栏就添加到了布局中。
最后,推荐一篇写的很好的博客:探索 Android View 绘制流程。
附录二:DecorVIew添加RootView
下面是DecorView添加基础布局的代码。
//DecorView.java#onResourcesLoaded
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
mDecorCaptionView = createDecorCaptionView(inflater);
//把传过来的根布局layoutResource进行解析并渲染
final View root = inflater.inflate(layoutResource, null); //通过inflate解析根布局layoutResource
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
//将其放在颜色视图下方
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //将rootView添加到进DecorView中并填满屏幕
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
2. 加载自定义布局
根布局加载完后,就开始来加载我们自己定义的布局了,例如加载activity_main.xml。
//PhoneWindow.java#setContentView
private LayoutInflater mLayoutInflater;
...
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//初始化mDecor和mContentParent
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
//去加载自己定义的layout
mLayoutInflater.inflate(layoutResID, mContentParent); //传入R.layout.activity_main和ContentView
}
...
}
//LayoutInflater.java#inflate
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
...
XmlResourceParser parser = res.getLayout(resource); //解析activity_main.xml布局文件
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
root.addView(temp, params); //将R.layout.activity_main添加到ContentView中
...
}
}
可以看到activity_main.xml已经加载到了Activity里面。
2、AppCompatActivity加载XML文件的过程
上一小节已经看过了Activity加载布局文件的过程,但是开发中,我们遇到AppCompatActivity的情况显然更多,接下来就看看AppCompatActivity和Activity加载布局文件的过程有何不同。
1. 初始化PhoneWIndow的mDecor和mDecorContentParent属性
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
//AppCompatActivity.java#setContentView
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
//AppCompatActivity.java#getDelegate
private AppCompatDelegate mDelegate;
...
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this); //当mDelegate为null时,创建mDelegate实例并返回
}
return mDelegate;
}
//AppCompatDelegate.java#create
public abstract class AppCompatDelegate {
...
public static AppCompatDelegate create(@NonNull Activity activity,
@Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback); //(1)
}
...
}
看到这里发现AppCompatActivityt并不是通过PhoneWindow设置布局的,而是通过AppCompatDelegate的实现类AppCompatDelegateImpl。
接下来我们来看看AppCompatDelegateImpl.java#setContentView是如何加载基础布局的。
//AppCompatDelegateImpl.java#setContentView
public void setContentView(View v) {
ensureSubDecor(); //初始化根布局
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content); //获取ContentView
contentParent.removeAllViews();
contentParent.addView(v);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
//AppCompatDelegateImpl.java#ensureSubDecor
ViewGroup mSubDecor; //mSubDecor可以对标Activity的mDecor
,,,
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
//mSubDecor的初始化
mSubDecor = createSubDecor();
...
onSubDecorInstalled(mSubDecor);
...
}
}
//AppCompatDelegateImpl.java#createSubDecor
private ViewGroup createSubDecor() {
...
mWindow.getDecorView();
...
}
//PhoneWindow.java#getDecorView
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor(); //初始化mDecor和mDecorContentParent
}
return mDecor;
}
我们看到AppCompatDelegateImpl也调用了PhoneWindow#installDecor方法来加载基础布局。
不过AppCompatDelegateImpl并没有直接使用installDecor的结果,而是在加载基础布局后,又做了一些操作。
我们继续看AppCompatDelegateImpl#createSubDecor方法。
2. 初始化AppCompatDelegateImpl的mSubDecor属性
//AppCompatDelegateImpl.java#createSubDecor
private ViewGroup createSubDecor() {
//获取当前应用主题中的数据,根据主题加载对应的xml文件,然后赋值给mSubDecor
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
...
mWindow.getDecorView(); //用和Activity的方式同样的方式加载基础布局
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
if (!mWindowNoTitle) {
if (mIsFloating) {
//如果我们是浮动的,请膨胀对话框标题装饰
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
//浮动窗口永远不能有操作栏,重置标志
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
...
//现在使用主题上下文膨胀视图并将其设置为内容视图
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.abc_screen_toolbar, null);
...
}
} else {
if (mOverlayActionMode) {
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null); //(1)
}
}
}
if (subDecor == null) {
//如果subDecor为null,则抛出异常
throw new IllegalArgumentException(...);
}
...
return subDecor;
}
①这里选择一个简单的xml做介绍,详见本节附录一
我们看到在PhoneWindow加载了基础布局后,进行了subDecor的创建,并将subDecor返回,单看这些,subDecor和AppCompatActivity并没有关联上。
其实在subDecor创建之后,AppCompatDelegateImpl#createSubDecor还对subDecor做了一些操作,操作完成后才返回subDecor。
我们来看看AppCompatDelegateImpl#createSubDecor在subDecor创建成功之后做了什么?
//AppCompatDelegateImpl.java#createSubDecor
private ViewGroup createSubDecor() {
...
subDecor = ...; //创建subDecor
...
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content); //(1)
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content); //(2)
if (windowContentView != null) {
//可能已经将视图添加到窗口的内容视图中,因此我们需要将它们迁移到我们的内容视图中
//------------ (3) ---------------
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
//更改我们的内容 FrameLayout 以使用android.R.id.content id.对片段有用。
//------------ (4) ---------------
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);
//装饰内容可能有一个前景可绘制对象集(windowContentOvelay)。删除它,因为我们自己处理它
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);(5)
}
}
//现在使用装饰设置窗口的内容视图
mWindow.setContentView(subDecor); //(6)
...
return subDecor;
}
//View.javaNO_ID
/**
* 用于标记没有 ID 的视图。
*/
public static final int NO_ID = -1; //(7)
① 获取subDecor加载的布局文件的ContentView,详见本节附录一。
② 获取当前PhoneWindow加载的基础布局文件中的ContentView。
③ 这段代码将PhoneWindow的ContentView中的所有子控件全部转移到subDecor中,转移后当前PhoneWIndow内的ContentView被清空。(这里注意,当前还未加载R.layout.activity_main,所以PhoneWindowd的ContentView可能是个空布局,这时就没必要转移,即直接跳过while循环。)
④ 这里进行了移花接木,将windowContentView的id设置为View.NO_ID(删除了id,见⑦),再将contentView的ID设为android.R.id.content,完成了id的替换,这时候通过findViewById(R.id.content)找到的就不是原来的windowContentView,而是subDecor的contentView,当然这里还差一个将subDecor绑定到PhoneWindow中的操作,这时候到步骤⑥了。
⑤ 设置windowContentView的setForeground为null,也就是windowContentView跳过绘制过程,这时没有id有无法绘制的windowContentView相当于一个空View。
⑥ 将subDecor绑定到PhoneWindow。
我们看看subDecor是如何绑定到PhoneWindow上的?
//AppCompatDelegateImpl.java#createSubDecor
private ViewGroup createSubDecor() {
...
mWindow.setContentView(subDecor);
...
}
//PhoneWindow.java#setContentView
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
//PhoneWindow.java#setContentView
public void setContentView(View view, ViewGroup.LayoutParams params) {
...
mContentParent.addView(view, params);
...
}
我们看到这时候subDecor就加到了原先的ContentView下面,现在的根布局结构如图:
![](https://img-blog.csdnimg.cn/329e453b71da400d9ee4865b3722081c.png#pic_center)
图2-2
3.加载自定义布局
这时候我们的AppCompatDelegateImpl#createSubDecor方法就看得差不多了,我们接着回到AppCompatDelegateImpl#ensureSubDecor。
//AppCompatDelegateImpl.java#ensureSubDecor
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
...
onSubDecorInstalled(mSubDecor);
mSubDecorInstalled = true;
...
}
}
//AppCompatDelegateImpl.java#onSubDecorInstalled
void onSubDecorInstalled(ViewGroup subDecor) {}
这里有个onSubDecorInstalled方法,他是一个空方法,该方法主要用来重写的,一般没有特殊需求,我们不会重写它。
调用完onSubDecorInstalled方法,mSubDecorInstalled就被设置为true了,这时候ensureSubDecor方法再被调用就会直接被跳过。
从名字也可以看出来,ensureSubDecor方法是为了确保subDecor已经初始化,也就是说很多必须使用subDecor的地方,都需要调用该方法来保证subDecor已经初始化了。
class AppCompatDelegateImpl extends AppCompatDelegate
implements MenuBuilder.Callback, LayoutInflater.Factory2 {
public void onPostCreate(Bundle savedInstanceState) {
// Make sure that the sub decor is installed
ensureSubDecor();
}
...
private void initWindowDecorActionBar() {
ensureSubDecor();
...
}
...
public <T extends View> T findViewById(@IdRes int id) {
ensureSubDecor();
return (T) mWindow.findViewById(id);
}
...
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
@Override
public void addContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.addView(v, lp);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
}
我们重点看AppCompatDelegateImpl#setContentView方法,可以看到他把subDecor的ContentView的子空间全部清空了,然后将传进来的View添加到了ContentView里面,也就是让我们传进来的View替换了原先ContentView的布局。AppCompatDelegateImpl#addContentView的逻辑类似,只不过不会清空原先的View,也就是在原有的基础上加上了传进来的View。
我们回忆一下AppCompatDelegateImpl#setContentView的调用方。
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
...
@Override
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(View view) {
initViewTreeOwners();
getDelegate().setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
initViewTreeOwners();
getDelegate().setContentView(view, params);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
initViewTreeOwners();
getDelegate().addContentView(view, params);
}
...
}
这时候我们又回到MainActivity了。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
再捋一下AppCompatActivity加载资源ID的路线。
//AppCompatActivity.java#setContentView
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
//AppCompatDelegateImpl.java#setContentView
public void setContentView(int resId) {
...
LayoutInflater.from(mContext).inflate(resId, contentParent);
...
}
//LayoutInflater#inflate
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
其实AppCompatActivity主要就是在Activity加载XML的基础上添加了SubDecor,其他的大差不差。
附录一:简单根布局abc_screen_simple.xml
<!-- abc_screen_simple.xml -->
<androidx.appcompat.widget.FitWindowsLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/action_bar_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.ViewStubCompat
android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/abc_action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/abc_screen_content_include" />
</androidx.appcompat.widget.FitWindowsLinearLayout>
<!-- abc_screen_content_include.xml -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.appcompat.widget.ContentFrameLayout
android:id="@id/action_bar_activity_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</merge>
3、UI绘制过程
现在XML已经加载好了,接下来谈谈UI具体的绘制过程,既然说到了绘制过程,那么肯定有一个生命周期是绕不开的也就是onResume。
我们先看看ActivityThread的handleResumeActivity方法。
(PS:这里笔者猜测ActivityThread#handleResumeActivity的调用方应该在TransactionExecutor.java#performLifecycleSequence,可以在上面的创建Activity实例小节中找到TransactionExecutor.java#executeCallbacks方法,不过之后的代码在小节中没有出现。可以自行依照executeCallbacks->cycleToPath->performLifecycleSequence的路径查找,该方法里面可以看到各种ActivityThread.java#handleXXXActivity生命周期方法的调用,笔者本来想要写入附录,但因为出现了很多疑问,所以暂且搁置,如果有机会,笔者会在了解过Activity整体的生命周期流程之后,再单独拿一篇出来讲讲Activity所有生命周期的流程。当然因为水平有限可能写的不好,这里只能恳请路过的大佬帮忙指点一二。)
//ActivityThread.java#handleResumeActivity
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
...
//待办事项 将 resumeArgs 推送到活动中以供考虑 跳过以下步骤进行双重恢复和 r.mFinish = true
if (!performResumeActivity(r, finalStateRequest, reason)) { //这里会执行Activity的onResume方法
return;
}
...
//获取Activity
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
//获取到Activity的PhoneWindow
r.window = r.activity.getWindow();
//获取到PhoneWindow的基础布局,也就是最外层的DecorView
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//获取到Activity的WindowManager对象
ViewManager wm = a.getWindowManager();
//获取到Activity的window的LayoutParams
WindowManager.LayoutParams l = r.window.getAttributes();
//把decorView从Activity记录类中拿出来交给Activity
a.mDecor = decor;
...
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
//通常,ViewRoot 使用 addView->ViewRootImpl#setView 中的活动设置回调。
//如果我们要重用装饰视图,则必须通知视图根目录回调可能已更改。
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//调用绘制方法
wm.addView(decor, l); //* 重点
} else {
//该活动将更早地获得此 {@link LayoutParams} 更改的回调。但是,届时不会设置装
//饰(在此方法中设置),因此不会执行任何操作。此调用可确保回调与装饰集一起发生。
a.onWindowAttributesChanged(l);
}
}
...
}
...
}
这里看完代码会发现Activity的onResume方法,其实是在UI绘制之前调用的,这点需要注意,一不小心就可能翻车。
关于这个坑,笔者再推荐一篇不错的博客:源码详细解析Activity生命周期onResume中Handler.Post(Runnable)和View.Post(Runnable)的UI效果差异原因
1. 调用Activity的onResume方法
接下来我们来看看Activity的onResume生命周期是如何被调用的。
//ActivityThread.java#handleResumeActivity
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
...
if (!performResumeActivity(r, finalStateRequest, reason)) { //这里会执行Activity的onResume方法
return;
}
}
//ActivityThread.java#performResumeActivity
public boolean performResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
String reason) {
...
r.activity.performResume(r.startsNotResumed, reason);
...
}
//Activity.java#performResume
final void performResume(boolean followedByPause, String reason) {
...
mInstrumentation.callActivityOnResume(this);
...
}
//Instrumentation.java#callActivityOnResume
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
...
}
现在我们Activity的onResume方法已经被调用了,接着就来看看Activity的显示流程。
(PS:这里performResumeActivity方法里面调用的不只是onResume方法,在此之前,如果Activity是重用的会先调用OnNewIntent方法,接着会判断Activity此前是否经历过onStop方法,如果有则会触发onRestart方法,然后调用onStart。这部分也会一并写到后面关于Activity生命周期流程的文章中。)
2. ViewRootImpl进行UI绘制的过程
(1) View绘制三大流程的入口
接下来我们来看看UI具体的绘制过程。
我们回到ActivityThread#handleResumeActivity方法,接着看看之ViewManager#addView方法是怎么来的。
//ActivityThread.java#handleResumeActivity
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, String reason) {
...
final Activity a = r.activity;
...
ViewManager wm = a.getWindowManager();
...
wm.addView(decor, l);
...
}
//Activity.java#getWindowManager
public WindowManager getWindowManager() {
return mWindowManager;
}
//Activity.java#mWindowManager
private Window mWindow;
private WindowManager mWindowManager;
...
final void attach(Context context, ActivityThread aThread, ...) {
...
mWindowManager = mWindow.getWindowManager();
...
}
//Window.java#getWindowManager
public WindowManager getWindowManager() {
return mWindowManager;
}
//Window.java#mWindowManager
private WindowManager mWindowManager;
...
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//WindowManagerImpl.java#createLocalWindowManager
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}
可以看到wm.addView方法中,wm变量其实是WindowManager的实现类WindowManagerImpl的对象。
看一下他的addView做了什么?
//WindowManagerImpl.java#addView
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
//WindowManagerGlobal.java#addView
@UnsupportedAppUsage
private final Object mLock = new Object();
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
...
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display, windowlessSession);
}
view.setLayoutParams(wparams);
//这三个集合里面储存着应用中所有Activity的基础属性
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//最后执行此操作,因为它会触发消息以开始执行操作
try {
root.setView(view, wparams, panelParentView, userId); //* 重点
} catch (RuntimeException e) {
//清理BadTokenException 或 InvalidDisplayException。
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
//ViewRootImpl.java#setView
View mView;
...
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
...
mView = view; //将DecorView赋值给mView
...
//安排第一个布局之前添加到窗口管理器,以确保我们在从系统接收任何其他事件之前进行重新布局。
//和View的requestLayout方法类似,它会重新进行测量->布局->绘制的过程
requestLayout();
...
view.assignParent(this); //(1)
...
}
//ViewRootImpl.java#requestLayout
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//检查线程
checkThread(); // (2)
mLayoutRequested = true;
//调度遍历
scheduleTraversals();
}
}
//ViewRootImpl.java#scheduleTraversals
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
...
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//调用线程mTraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
//ViewRootImpl$TraversalRunnable.java#run
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//执行调度
doTraversal();
}
}
//ViewRootImpl.java#doTraversal
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals(); //View绘制的测量、放置、绘制三大流程,就是在这里发生的
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
//ViewRootImpl.java#performTraversals
private void performTraversals() {
//缓存mView,也就是DecorView,因为它在下面使用了很多...
final View host = mView;
...
//询问Host想要多大 这里是绘制的开始
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw()
...
}
①这里是将RootViewImpl添加为DecorView的父布局,详见附录一。
① 这里是线程检查,也就是检查是否为UI线程,详见附录二。
可以看到先测量、再布局、最后绘制,这个流程的顺序是由ViewRootImpl.java#performTraversals 方法确定的。
接下来我们就来具体看看这三个流程的调用。
(2) View绘制三大流程之onMeasure方法
//ViewRootImpl.java#performMeasure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//调用DecorView的measure方法,这里会层层传递,测量整个Activity里面的所有View
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
(3) View绘制三大流程之onLayout方法
//ViewRootImpl.java#performLayout
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
final View host = mView;
...
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
public void layout(int l, int t, int r, int b) {
...
onLayout(changed, l, t, r, b);
...
}
(4) View绘制三大流程之onDraw方法
//ViewRootImpl.java#performDraw
private boolean performDraw() {
...
boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
...
}
//ViewRootImpl.java#draw
private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
...
}
//ViewRootImpl.java#drawSoftware
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
//使用软件渲染器绘制。
final Canvas canvas;
...
canvas = mSurface.lockCanvas(dirty);
...
mView.draw(canvas);
...
}
//ViewRootImpl.java#draw
public void draw(Canvas canvas) {
...
onDraw(canvas);
,,,
}
附录一:将ViewRootImpl添加为DecorView的父布局
ViewRootImpl在setView中会被添加为根布局DecorView的父亲,下面看看添加的流程。
//ViewRootImpl.java# setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
...
requestLayout();
...
view.assignParent(this); //为DecorView分配父布局
...
}
//View.java#assignParent
protected ViewParent mParent;
...
/**
* 调用方负责在必要时调用 requestLayout。(这允许 addViewInLayout 不请求新布局。)
*/
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
/**
* 视图层次结构的顶部,在视图和窗口管理器之间实现所需的协议。这在很大程度上是WindowManagerGlobal的内部实现细节
*/
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
AttachedSurfaceControl {
...
}
可以看到ViewRootImpl是整个布局结构的根节点,它管理整颗View树的测量、布局、绘制等过程,是View和WindowManger之间的沟通的桥梁。
值得一提的是,ViewRootImpl本身并不是View,只是实现了ViewParent接口,所以DecorView依旧是最顶层的View,是所有View的祖先节点。
附录二:UI线程检查
我们应该对UI线程都不陌生,那么UI线程究竟是怎么判断的?我们来看看ViewRootImpl.java的checkThread方法。
//ViewRootImpl.java#checkThread
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
//ViewRootImpl.java#mThread
final Thread mThread;
...
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
...
mThread = Thread.currentThread();
...
}
可以看到UI线程,其实就是创建ViewRootImpl的线程,在回到上面讲的启动主线程的Looper。
这时候对比一下,就可以发现主线程是APP进程的第一个线程,而UI线程是创建ViewRootImpl的线程,也就是说两者不一定相同,这也就意味着在子线程修改UI,或者说让子线程成为UI线程,其实也是可行的。有兴趣的可以转到这里你不知道的,Android子线程中的UI操作
那么再换个思路,我们已经找到ViewRootImpl是在ActivityThead#onResume方法之后才初始化的,那么在此之前,UI不受ViewRootImpl控制,也就是说,不会有UI线程安全的问题,那么这就意味着我们在这段时间内是可以使用子线程修改UI的,当然这时候UI还没有绘制,相关内容可以转到这里震惊!Android子线程也能修改UI?