彻底搞懂UI绘制流程,看该系列就够了
前言
作为安卓开发最重要的知识点之一,UI绘制无疑是必须掌握的,要想搞懂它的测量、布局和绘制,得先理解它的整个流程,但现在让我们把时间再往前拨一下,先要从App启动流程以及Activity启动流程讲起。
提示:以下是本篇文章正文内容
一、App启动流程
在 手把手带你搞懂AMS启动原理 中讲解AMS以及PMS的时候,就已经知道整个app启动过程,所有参与的类以及它们的关系流程:
对于这个流程如果不熟,可以看回 手把手带你搞懂AMS启动原理 。而现在这一节就是着重讲解这个被fork出来的App应用是怎么具体启动的。
先从ActivityThread类的main()方法看起,它是整个App应用进程的入口方法,相当于java程序的main()方法那样,在主线程(UI线程)被调用:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在讲这个方法前还要介绍一下ActivityThread类里有个内部类ApplicationThread,它是ActivityThread与AMS通信的桥梁,从上面的图来看可以知道AMS和ActivityThread进行通信时,是通过ApplicationThread作为中间者来回传数据的。
构造Application对象为什么不直接在ActivityThread里通过new一个Application对象这样去构造呢,而要这么麻烦跨进程去跟AMS进行通信。因为我们还要考虑自定义Application这种情况,以及一些性能消耗等方面,所以就要依靠PMS去解析清单文件里的Application信息和组件信息,然后传给AMS,之后AMS再把构造的这个Application对象需要的数据传给ActivityThread,但因为它们是不同进程的,因此需要Binder机制去进行跨进程通信,其中ApplicationThread就在这个通信过程中扮演中间者的角色,接收AMS的指令并执行ActivityThread的方法,然后将数据回调给ActivityThread。
前面在main方法可以看到调用了:
Looper.prepareMainLooper();
因为ActivityThread是继承ClientTransactionHandler,因此这里会采用Handler机制,调用prepareMainLooper()方法初始化Looper:
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
这里面首先调用了prepare(false)方法,设置了当前线程不可退出,然后调用myLooper()方法将主线程赋值给sMainLooper。
最后就是进行消息轮询:
Looper.loop();
既然它是Handler,看看它继承的类:
public abstract class ClientTransactionHandler {
...
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
...
/** Destroy the activity. */
public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished,
boolean userLeaving, int configChanges, PendingTransactionActions pendingActions,
String reason);
/**
* Resume the activity.
* @param r Target activity record.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param isForward Flag indicating if next transition is forward.
* @param reason Reason for performing this operation.
*/
public abstract void handleResumeActivity(@NonNull ActivityClientRecord r,
boolean finalStateRequest, boolean isForward, String reason);
...
*/
public abstract void handleStopActivity(@NonNull ActivityClientRecord r, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
...
}
里面有各种handleXXXActivity()这些方法,当PMS和AMS把要启动的Activity的类信息通过binder机制传回给ActivityThread后,ActivityThread就通过Handler机制对各种消息进行对应的生命周期方法的处理,比如要运行Activity时,就会调用handleResumeActivity()方法触发Activity的onResume()方法。
接着继续看main()方法,其中这两句代码很重要:
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
...
new了ActivityThread对象,然后调用了attach()方法:
@UnsupportedAppUsage
private void attach(boolean system, long startSeq) {
...
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
...
里面是调用了ActivityManager的getService方法去构造了一个AMS的代理对象,如果有看过 手把手带你搞懂AMS启动原理 就知道,它就是使用Binder机制去获取这个代理对象,有了它,就等于可以操作到AMS,跟它进行通信:
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static IActivityTaskManager getTaskService() {
return ActivityTaskManager.getService();
}
@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
对于BInder机制这块如果有什么疑问,可以观看我的Binder系列:
Binder机制总结(上篇)–java层与框架层分析
Binder机制总结(下篇)–驱动层分析
我们继续往回看,在得到AMS代理对象后,会调用它的attachApplication()方法:
这里在调用AMS的attachApplication方法时,传入了一个参数mAppThread,它就是ApplicationThread,现在去看IActivityManager的实现类的attchApplication方法:
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread,callingPid,callingUid,startSeq);
Binder.restoreCallingIdentity(origId);
}
}
从binder中获取到进程信息后,接着调用attachApplicationLocked方法:
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
} else if (app.instr != null) {
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
}
...
}
代码很多,但拉到后面可以看到调用了ActivityThread的bindApplication()方法,方法里的有参数appInfo等,这样app相关的信息数据都回传给ActivityThread,那这样就要看该方法的详情:
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
SharedMemory serializedSystemFontMap,
long startRequestedElapsedTime, long startRequestedUptime) {
...
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.sdkSandboxClientAppVolumeUuid = sdkSandboxClientAppVolumeUuid;
data.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage;
data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillOptions = autofillOptions;
data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
data.mSerializedSystemFontMap = serializedSystemFontMap;
data.startRequestedElapsedTime = startRequestedElapsedTime;
data.startRequestedUptime = startRequestedUptime;
sendMessage(H.BIND_APPLICATION, data);
}
把在AMS里回传过来的app应用的信息封装在AppBindData里,然后把它发送出去,也就是调用sendMessage方法,它是mH发送的:
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);
}
mH是ActivityThread的内部类H,它也是继承Handler:
class H extends Handler {
...
}
所以它里面会有处理BIND_APPLICATION消息的方法:
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);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
里面调用了handleBindApplication方法:
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
...
// Allow disk access during application and provider setup. This could
// block processing ordered broadcasts, but later processing would
// probably end up doing the same disk access.
Application app;
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
...
...
该方法代码很多,但只需看上面那部分,先是定义了Application对象app,然后调用makeApplicationInner()方法最后赋值给app,我们来跟踪一下makeApplicationInner()方法:
public Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
return makeApplicationInner(forceDefaultAppClass, instrumentation,
/* allowDuplicateInstances= */ false);
}
继续看makeApplicationInner()方法:
private Application makeApplicationInner(boolean forceDefaultAppClass,
Instrumentation instrumentation, boolean allowDuplicateInstances) {
...
Application app = null;
final String myProcessName = Process.myProcessName();
String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
myProcessName);
...
try {
...
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
}
...
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
}
可以看到调用了mInstrumentation对象的newApplication()方法去构造app对象:
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
在这里能看到类加载器,以及app类名传到instantiateApplication()方法:
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Application) cl.loadClass(className).newInstance();
}
没错,最后就是用反射去构造Application对象。
既然构造好了Application对象,之后肯定是要调用它的onCreate等这些生命周期方法,所以我们接着看handleBindApplication()方法的源码,可以看到:
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
还是通过Instrumentation去调用,这里调用了callApplicationOnCreate()方法:
public void callApplicationOnCreate(Application app) {
app.onCreate();
}
可以看到,调用了app对象的onCreate方法。
二、Activity的启动
现在我们可以知道Application的启动最关键其实是在于AMS去调用了ActivityThread里的ApplicationThread的bindApplication()方法:
也就是这个时候把需要启动的App信息都回传到ApplicationThread里,然后再封装在Handler的消息里发送给ActivityThread去调用Instrumentation去构造和启动App。
所以,启动Activity时也是跟启动App一样的过程,也就是AMS的attachApplicationLocked()方法里调用的,往下看:
...
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
...
在调用完bindApplication()方法后,紧接着就是调用了mStackSupervisor.attachApplicationLocked()方法,这里的mStackSupervisor,它其实是一个Activity栈来的:
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
RecentTasks.Callbacks {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
...
里面封装了每个Activity的信息,那我们就来看看它调用的方法attachApplicationLocked()方法是怎样的:
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
...
final ActivityRecord top = stack.topRunningActivityLocked();
final int size = mTmpActivityList.size();
for (int i = 0; i < size; i++) {
final ActivityRecord activity = mTmpActivityList.get(i);
if (activity.app == null && app.uid == activity.info.applicationInfo.uid
&& processName.equals(activity.processName)) {
try {
if (realStartActivityLocked(activity, app,
top == activity /* andResume */, true /* checkConfig */)) {
didSomething = true;
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ top.intent.getComponent().flattenToShortString(), e);
throw e;
}
}
}
...
}
遍历整个Activity栈,调用topRunningActivityLocked()返回栈中最顶部Activity(也就是首个要启动的Activity),赋值给top,此时这个ActivityRecord类对象top还不是一个真正的Activity类,它只是用来存储该启动的Activity的相关信息。然后它被传进了realStartActivityLocked()方法,看看这个方法的详情:
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
...
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
...
}
它在里面创建了启动Activity事务对象,传了app.thread(也就是ApplicationThread对象),是用来等下回调用的,紧接着调用addCallback()方法,目的是用该事务对象添加回调接口,传了一个LaunchActivityItem对象进去,接下来肯定就是提交事务对象:
了解事务机制都知道,既然是提交了事务,那肯定是要有地方去调度(处理)这个提交过来的事务,所以来看这个ClientLifecycleManager的scheduleTransaction()方法:
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();
}
}
里面看到调用了transaction的schedule()方法:
可以看到了吧,调用的就是ApplicationThread对象的scheduleTransaction()方法,那这个mClient是什么时候赋值的,就是在构造事务对象时传的:
app.thread对象就是这个时候传进去的。因此看到这里,相信大家都知道AMS在启动Activity时,也是AMS将Activity相关信息回调给ActivityThread的ApplicationThread类,调用它的方法,然后来构造和启动Activity,所以最终Activity类相关的信息回调给了ActivityThread。那我们接下来看看ApplicationThread的scheduleTransaction()方法:
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
它里面调用了ActivityThread的scheduleTransaction()方法,其实这里是调用了ActivityThread父类ClientTransactionHandler的scheduleTransaction()方法:
public abstract class ClientTransactionHandler {
// Schedule phase related logic and handlers.
/** Prepare and schedule transaction for execution. */
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
...
}
正如我们讲Application启动时那样,ActivityThread和ApplicationThread通信是使用Handler机制的,所以可以看到scheduleTransaction()方法里发送了EXECUTE_TRANSACTION消息,那么就去看看H类的处理该消息的方法:
...
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
...
从消息对象里把AMS传过来的Activity类信息封装在事务对象transaction里,然后调用了TransactionExecutor的execute方法,执行了这个事务对象:
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
executeCallbacks(transaction);
executeLifecycleState(transaction);
mPendingActions.clear();
log("End resolving transaction");
}
里面看到调用了executeCallbacks()方法:
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
...
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
log("Resolving callback: " + item);
final int postExecutionState = item.getPostExecutionState();
final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
item.getPostExecutionState());
if (closestPreExecutionState != UNDEFINED) {
cycleToPath(r, closestPreExecutionState);
}
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
if (r == null) {
// Launch activity request will create an activity record.
r = mTransactionHandler.getActivityClient(token);
}
...
}
...
}
可以看到这里是遍历callbacks,然后从它里面取出一个个回调接口类对象ClientTransactionItem,那这个callbacks是什么时候添加这些回调接口类的,其实是在构造事务对象的时候:
添加的是LaunchActivityItem对象,而LaunchActivityItem是继承ClientTransactionItem:
public class LaunchActivityItem extends ClientTransactionItem {
private Intent mIntent;
private int mIdent;
private ActivityInfo mInfo;
private Configuration mCurConfig;
private Configuration mOverrideConfig;
private CompatibilityInfo mCompatInfo;
private String mReferrer;
private IVoiceInteractor mVoiceInteractor;
private int mProcState;
...
所以这时候遍历callbacks,然后从它里面取出一个个回调接口类对象ClientTransactionItem这句代码其实就是父类引用指向子类对象,因此接着往下看:
item.execute(mTransactionHandler, token, mPendingActions);
调用了item的execute()方法,因此我们现在去看LaunchActivityItem的execute()方法:
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
这里可以看到创建了ActivityRecord对象,用于Activity的实例化。另外还有个参数ClientTransactionHandler类,它其实就是ActivityThread,因为别忘了ActivityThread是继承ClientTransactionHandler的,因此现在调用了client的handleLaunchActivity()方法,其实就是调用了ActivityThread的handleLaunchActivity()方法,传进了ActivityClientRecord对象,它封装了由AMS传过来的这个启动Activity类相关信息,用于Activity的实例化。继续看ActivityThread的handleLaunchActivity()方法:
/**
* Extended implementation of activity launch. Used when server requests a launch or relaunch.
*/
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
final Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
if (!r.activity.mFinished && pendingActions != null) {
pendingActions.setOldState(r.state);
pendingActions.setRestoreInstanceState(true);
pendingActions.setCallOnPostCreate(true);
}
...
}
看到这个performLaunchActivity()方法:
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ComponentName component = r.intent.getComponent();
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
...
可以看到activity对象是由Instrumentation对象的newActivity()方法得到:
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
String pkg = intent != null && intent.getComponent() != null
? intent.getComponent().getPackageName() : null;
return getFactory(pkg).instantiateActivity(cl, className, intent);
}
意图Intent对象也是这个时候传进来供Instrumentation参考使用,所以如果要改变Activity的启动意图,是不是可以考虑反射这个Intent,至于这里的Intent对象又是哪里赋值的,大家有兴趣可以往回找找看。最终调用了instantiateActivity()方法:
public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Activity) cl.loadClass(className).newInstance();
}
所以跟创建Application对象一样,也是使用类加载器ClassLoader去反射构造Activity对象。既然现在成功把Activity对象构造出来了,那接下来就是要启动它了:
...
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
...
我们继续往回看,跟踪Instrumentation对象,可以看到它调用了callActivityOnCreate()方法:
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
调用了Activity的performCreate()方法:
final void performCreate(Bundle icicle) {
performCreate(icicle, null);
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
mActivityTransitionState.readState(icicle);
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
}
可以看到,最后就是调用了Activity的onCreate()方法。
现在来总结一下整个Application启动和Activity启动的流程:
从attachApplication方法起,ActivityThread与AMS就进行通信,AMS持有ActivityThread的ApplicationThread引用,而ActivityThread也持有了AMS的代理引用,然后AMS把要启动的Application类相关信息数据都通过调用ApplicationThread的bindApplication()方法回传给ActivityThread那边,然后ApplicationThread拿到这些数据之后就开始跟ActivityThread进行消息通信,然后ActivityThread就通过Instrumentation进行构造Application对象和启动Application的生命周期方法,接着就是启动Activity,过程类似,不同的是多了一个事务机制:
AMS会创建一个事务对象去把要启动的Activity类相关数据通过事务形式提交给ActivityThread那边,当然也是ApplicationThread去调度处理这个事务,然后又是通过Handler机制把数据封装在消息对象里供ActivityThread提取出来处理,然后ActivityThread通过Instrumentation对象去构造Activity对象和启动Activity生命周期方法。
想要完整的流程总结图可以关我公号:Pingred
三、UI绘制流程
当一个Activity启动之后(从onCreate()方法执行),接下来就是去解析xml布局文件,然后进行测量、布局和绘制的过程,我们现在先来看看当Activity继承Activity的情况,从onCreate()方法里的setContentView()开始看:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
里面调用了window(getWindow()方法获得)的setContentView()的方法,这个window是个抽象类:
它里面有两个很重要的属性:
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
...
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
...
DecorView是顶层视图,也就是最外层的视图,而ViewGroup要么就是DecorView本身,要么就是DecorView的子层布局。现在再回过头看phoneWindow的setContentView()方法:
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
因为当前我们第一次加载布局,肯定mContentParent为空的,所以调用installDecor()方法,初始化顶层布局DecorView,看看installDecor()方法的详情:
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
...
可以看到,先是构造了mDecor对象,调用了generateDecor()方法:
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
实则就是new了DecorView对象,继续跳出去看setContentView()方法,构造了mDecor对象之后,就是把它传到generateLayout()方法里去,用来初始化mContentParent对象,generateLayout()方法的详情:
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
if (false) {
System.out.println("From style:");
String s = "Attrs:";
for (int i = 0; i < R.styleable.Window.length; i++) {
s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
+ a.getString(i);
}
System.out.println(s);
}
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
...
首先是调用getWindowStyle方法获取窗体样式,然后再根据这些样式去判断要给布局id设置哪个去加载布局,其中我们以这个最常见的布局去分析:
...
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
}
...
else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
...
当常规的布局id就是设置成R.layout.screen_simple,那我们来看看这个布局是什么样的:
<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>
现在layoutResource赋值了这个布局,紧接着就是:
...
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
调用了onResourcesLoaded()方法去加载这个布局,传的参数有LayoutInflater以及layoutResource:
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
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 {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
没错,就是用LayoutInflater去inflate解析R.layout.screen_simple布局文件,然后将解析出来的布局通过addView()方法添加给root,也就是DecorView:
viewStub就是状态栏,然后FrameLayout就是内容布局。再跳回generateLayout()方法,接着往下看:
. ..
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
现在mDecor有了,就给它的子级布局contentParent对象给构成出来,用的是一个ID_ANDROID_CONTENT:
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
它其实就是R.layout.screen_simple布局里的FrameLayout:
那我倒回setContentView()方法看,在把mDecor的子级布局contentParent对象给构造出来后,就可以加载我们开发者自己的布局了:
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
调用inflate()方法,将我们自己的布局id和这个conteParent传进去,记住此时这个conteParent其实就是这个FrameLayout:
我们现在就是把自己定义的布局给解析添加到这个FrameLayout里去:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
把布局文件xml里的节点解析成parser对象后,再传到inflate方法里:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
...
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
...
看到最后,通过addView()方法把我们的布局添加进mContentParent里去了。
整个过程其实并不复杂,因为它没有很多分支,都是一路看跟着源码方法跟踪分析就好。
那如果Activity继承的是AppCompatActivity,那整个加载布局的流程又是怎样的,我们继续来看:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
我们继续跟踪setContentView()方法:
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
这里跟继承Activity时不一样,不再是phoneWindow的setContentView方法,而是AppCompatDelegate的setContentView方法,而AppCompatDelegate是个抽象类:
所以我们来跟踪它的实现类AppCompatDelegateImpl的setContentView()方法:
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
先来看看这个ensureSubDecor()方法:
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
// If a title was set before we installed the decor, propagate it now
CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
if (mDecorContentParent != null) {
mDecorContentParent.setWindowTitle(title);
} else if (peekSupportActionBar() != null) {
peekSupportActionBar().setWindowTitle(title);
} else if (mTitleView != null) {
mTitleView.setText(title);
}
}
...
}
}
这里可以看到mSubDecor(DecorView)对象是通过createSubDecor()方法获得的,因此继续跟踪createSubDecor()方法:
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
a.recycle();
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
}
...
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);
}
...
}
可以看到,它也是根据不同窗体主题样式来加载不同的布局,然后构建出DecvorView对象,那这里就以最常见的样式布局来分析,加载的是R.layout.abc_screen_simple布局:
<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>
这个布局文件似乎跟R.layout.screen_simple布局文件有点不一样,其实多出了这一层的FitWindowsLinearLayout是兼容布局,为了兼容之前版本而加的布局,所以可以不用纠结它,我们来看里面嵌套了ViewStubCompat组件它也是表示状态栏,另一个abc_screen_content_include组件点进去看它长什么样子:
<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>
它里面有个id为action_bar_activity_content的组件ContentFrameLayout,也是个帧布局。
现在通过加载R.layout.screen_simple布局文件然后构造出DecvorView对象后,接着createSubDecor()方法往下看:
...
// Make the decor optionally fit system windows, like the window's decor
ViewUtils.makeOptionalFitsSystemWindows(subDecor);
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
// Change our content FrameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);
...
通过DecvorView对象(subDecor)把它里面的action_bar_activity_content组件给构造出contentView对象,也就是刚刚在abc_screen_content_include组件里面看到的action_bar_activity_content组件。得到这个对象之后,下面就又通过mWindow去加载id为content的组件,它就是我们分析继承Activity时加载布局时见到的那个R.layout.screen_simple布局文件里的content组件:
此时把它加载出来,赋值到windowContentView对象,然后就是这两句关键代码:
它把windowContentView设置成了NO_ID(-1),也就是设置成了没有布局,然后contentView就设置成了content布局,所以现在decoview布局里的ContentFrameLayout就是content布局:
我们现在再次返回到setContentView()方法:
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
ensureSubDecor()方法完成后,此时的mSubDecor里就有content这个子布局组件了,因此通过findViewById可以创建出contenParent对象出来,接着就可以把我们自己定义的布局id和contenParent传给inflate()方法里去,让contenParent添加我们的布局,这个过程跟继承Activity一样了,这里就不作复述了。
1、测量
既然现在在Actitivty的onCreate方法里,已经是把我们定义的xml布局文件解析并且添加到我们的contentParent对象里去了,那现在该是将这些组件进行测量、布局以及绘制,最后呈现给用户看。那么这几个过程其实都是在Acitivity的onResume()方法里进行的。
之前通过分析Activity的启动(onCreate方法)时可以知道,Activity的生命周期方法的调用其实是ActivityThread调用的,所以onResume方法也是在ActivityThread里调用的:
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
...
可以看到,从Activity对象里依次获取window对象,然后获取decorView对象(onCreate()方法里解析布局xml得到的),接着获取windowManager对象,调用它的addView()方法:
...
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
...
因为windowManager是抽象类,因此跟踪它的实现类WindowManagerImpl的addView()方法:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
继续跟踪addView()方法:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
可以留意到这句root.setView(view, wparams, panelParentView);
其实就是把devorView传到setView()方法里去,来看看这个方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
它是ViewRootImpl的方法,接着往下看可以看到:
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
...
在里面可以看到调用了requestLayout()方法,这个方法相信大家都不陌生了,我们在自定义控件的时候会调用它来进行重新测量和布局以及绘制的。我们看看它的详情:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
继续跟踪scheduleTraversals()方法:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
这里是post了一个线程,mTraversalRunnable,看看它的run方法:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
run方法里调用了doTraversal()方法:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
答案就在这个performTraversals()方法里了,继续跟踪performTraversals()方法:
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}
...
代码很多,但往下看,可以看到里面调用了performMeasure()方法:
...
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
...
看看它的详情:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
最终就是调用了控件的measure()方法,然后就是遍历父控件里的所有子控件,一个个进行测量。这部分测量的具体分析讲话在下篇系列继续为大家详细讲解。我们往回继续看performTraversals()方法,往下看就是调用了performLayout()方法:
...
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, mWidth, mHeight);
...
...
没错,这个就是组件的布局方法:
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;**
...
遍历父布局里的子控件,然后进行layout()方法。那么控件的绘制方法也是在performTraversals()方法里,继续往下看:
...
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
} else {
if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
如你所见,调用了performDraw()方法,那么现在就可以知道其实我们平常调用的requestLayout()方法的整个流程就是布局的测量、布局与绘制顺序就是分别调用了performLayout()方法、performLayout()方法和performDraw()方法。
整个流程其实并不复杂,想要App启动的流程图、Activity启动的流程图以及上面的UI绘制流程图,可关我公号:Pingred文章来源:https://www.toymoban.com/news/detail-573697.html
另外下篇将为大家详细分析View的测量measure()方法、layout()以及performDraw()方法里做了什么。文章来源地址https://www.toymoban.com/news/detail-573697.html
到了这里,关于UI绘制流程分析(前篇)--App与Activity的启动的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!