本篇为笔者对Android SDK 33版本的UI绘制入口进行追踪的过程,主要作笔记作用。由于笔者经验尚浅,水平也有限,所以会存在很多不足的地方。如果有大神发现问题,可以在评论区指出。
章节目录:
一、APP程序入口
1、 ActivityThread的main方法、重要属性及其父类的介绍
1. ActivityThread及其main函数的介绍
2. ActivityThread重要属性的介绍
3. 启动主线程的Looper
4. ActivityThread的父类ClientTransactionHandler
2、 Application的创建与启动
1. 创建ActivityThread实例
2. 通过AMS获取Application信息
3. 创建Instrumentation实例
4. 创建Application实例
5. 启动Application
3、Activity的创建和启动
1. 系统进程的准备工作
(1) ActivityTaskManagerService
(2) WindowContainer
(3) ClientTransaction
[1] 初始化mDecor属性
[2] 初始化mContentParent属性
附录:ActivityTaskManagerService的启动
2. 创建Activity实例
附录:AppComponentFactory
3. 启动Activity
二、UI绘制流程
1、Activity加载XML文件的过程
1. PhoneWindow的初始化
(1) 创建PhoneWindow
(2) PhoneWindow的重要属性——mDecor和mContentParent
(3) mDecor和mContentParent属性的初始化
附录一:常见的根布局——screen_simple.xml
附录二:DecorVIew添加RootView
2. 加载自定义布局
2、AppCompatActivity加载XML文件的过程
1. 初始化PhoneWIndow的mDecor和mDecorContentParent属性
2. 初始化AppCompatDelegateImpl的mSubDecor属性
3.加载自定义布局
附录一:简单根布局abc_screen_simple.xml
3、UI绘制过程
1. 调用Activity的onResume方法
2. ViewRootImpl进行UI绘制的过程
(1) View绘制三大流程的入口
(2) View绘制三大流程之onMeasure方法
(3) View绘制三大流程之onLayout方法
(4) View绘制三大流程之onDraw方法
附录一:将ViewRootImpl添加为DecorView的父布局
附录二:UI线程检查
/**
* 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");
}
...
}
首先我们需要知道一点,ActivityThread不会持有Activity或者ActivityThread里面的任何东西,真正管理我们application或者activity的是AMS或者PMS。ActivityThread就是一个handler,它会不停的和我们的AMS或者PMS进行通信,然后得到结果,再来通过消息队列来执行它的handlerMessage。
我们接着看ActivityThread的几个主要的属性:
public final class ActivityThread extends ClientTransactionHandler {
//ApplicationThread是ActivityThread与AMS通讯的桥梁,它作为服务端,接收ActivityManagerService的指令并执行
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
...
//真正管理Activity生命周期的类
Instrumentation mInstrumentation;
}
//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
个人见解: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模式:
这里有一点值得注意,Activity是在主线程中创建的,也就说ActivityThread的main方法所在的线程就是APP的主线程,这点在上面看到ActivityThread的Looper时就可见一斑。因为主线程是main方法所在的线程,也就是APP进程的第一个线程,所以主线程无法修改。
我们启动应用,就必须先创建Application。由于不确定项目中具体的Application类,所以不能通过new来创建,而是需要通过AMS去获取Application的类名信息,再通过反射将它实例化;若AMS获取不到,再使用new的方式创建默认的Application。
//ActivityThread.java#main
ActivityThread thread = new ActivityThread();//创建一个ActivityThread
thread.attach(false, startSeq);
我们先看看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方法。
//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);
}
...
}
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。
我们继续看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();
}
前面我们在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);
}
}
}
...
}
//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的?
//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;
}
可以在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();
...
}
//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的全类名,然后根据类加载器来进行实例化。
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文件标签的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;
}
现在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页面了。
上一小节,我们已经追踪到了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方法
在追踪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。
接下来看看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);
...
}
}
先看一下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对象。
接着看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都完成了初始化。
下面就是最常见的根布局XML文件的代码。
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。
接下来特别说说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添加基础布局的代码。
//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();
}
根布局加载完后,就开始来加载我们自己定义的布局了,例如加载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里面。
上一小节已经看过了Activity加载布局文件的过程,但是开发中,我们遇到AppCompatActivity的情况显然更多,接下来就看看AppCompatActivity和Activity加载布局文件的过程有何不同。
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方法。
//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下面,现在的根布局结构如图:
这时候我们的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,其他的大差不差。
<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>
<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>
现在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效果差异原因
接下来我们来看看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生命周期流程的文章中。)
接下来我们来看看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 方法确定的。
接下来我们就来具体看看这三个流程的调用。
//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);
...
}
//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);
...
}
//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在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线程究竟是怎么判断的?我们来看看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?