注:本文分析的源码版本为Android 27
java
若有转载,请标明出处android
(更新时间:2021-6-11)
数组
首先,咱们须要知道:缓存
ActivityThread
的main
方法,是Android应用程序启动时的入口点。markdown
public final class ActivityThread {
// 省略部分代码
public static void main(String[] args) {
// 省略部分代码
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
复制代码
在main方法里执行了如下操做:app
Looper.prepareMainLooper()
方法,它会建立一个与当前线程(主线程)相关联的Looper对象。ActivityThread
对象,并调用其attach()
方法。Looper.loop()
方法,让刚建立的Looper对象,从它的MessageQueue中循环读取数据并执行。如今,咱们开始分析ActivityThread的attach方法作了什么?ide
public final class ActivityThread {
final ApplicationThread mAppThread = new ApplicationThread();
// 省略部分代码
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
// 省略部分代码
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// 省略后续代码
}
}
}
复制代码
在attach方法中,调用ActivityManager.getService()
方法,获取到远程的IActivityManager
对象,并将一个ApplicationThread
实例传入。oop
ApplicationThread继承至IApplicationThread.Stub
,即Binder进程间通讯的本地实现类。它有不少与Activity生命周期相关的方法,大都以scheduleXXX
命名:布局
至此,咱们能够总结一下:ui
ActivityThread
的main
方法,是应用程序启动的入口。Binder
机制,回调到ApplicationThread
的scheduleXXX
方法中。ActivityThread
和ApplicationThread
针对每一个应用,都只有一个实例。这里有个疑问:
为何没有
scheduleStartActivity
方法?难道Activity的onStart()
生命周期回调,不是由ActivityManager
发送消息控制的?
带着这个疑问,咱们开始阅读这些scheduleXXX方法的源码。
咱们从ApplicationThread中的scheduleLaunchActivity
方法开始分析,由于由名字能够猜想它应该会执行Activity建立和启动工做。
public final class ActivityThread {
// 省略部分代码
private class ApplicationThread extends IApplicationThread.Stub {
// 省略部分代码
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
// 给ActivityClientRecord赋值
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
// 省略部分代码
}
}
复制代码
在scheduleLaunchActivity方法中,首先建立了一个ActivityClientRecord
对象,并对其进行赋值。
ActivityClientRecord:用于记录与Activity相关的数据。
而后经过Handler,将线程由Binder线程
切换到主线程
。最终调用到ActivityThread的handleLaunchActivity
方法。
public final class ActivityThread {
// 省略部分代码
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 省略部分代码
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
// 省略部分代码
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);
// 省略部分代码
}
}
}
}
复制代码
这里,咱们暂时无论performLaunchActivity
方法中作了什么,仅分析后续代码。后续代码中,调用了handleResumeActivity
,猜想它应该会调用Activity的onResume
方法。
根据Activity生命周期推测到:
在
performLaunchActivity
方法里,必定会依次调用Activity的onCreate
、onStart
方法。
带着这个思路,开始分析performLaunchActivity方法。
public final class ActivityThread {
// 省略部分代码
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);
// 省略部分代码
} catch (Exception e) { /* 省略部分代码 */ }
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// 省略部分代码
if (activity != null) {
// 省略部分代码
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
// 省略部分代码
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
// 省略部分代码
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
// 省略部分代码
}
}
r.paused = true;
// 保存ActivityClientRecord
mActivities.put(r.token, r);
} catch { /* 省略catch相关代码 */ }
return activity;
}
复制代码
上述代码主要执行了如下操做:
调用Instrumentation
的newActivity
方法,经过反射建立Activity对象。
调用Activity对象的attach
方法,用于初始化Activity的一些数据,同时会为Activity设置Window
对象。 注意
:Activity的Window对象,与传入的Window对象不是同一个对象。这也意味着:每一个Activity都有各自的Window对象
。
public class Activity extends .... {
// 省略部分代码
private Window mWindow;
private WindowManager mWindowManager;
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
// 省略部分代码
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
// 省略部分代码
mWindowManager = mWindow.getWindowManager();
// 省略部分代码
}
// 省略部分代码
}
复制代码
一、
Instrumentation.callActivityOnCreate
方法,该方法中会调用activity.performCreate()
方法。
二、activity.performStart()
方法。
三、Instrumentation.callActivityOnPostCreate
方法,该方法中会调用activity.onPostCreate()
方法。
查看里面的源码,确实依次调用了onCreate
、onStart
、onPostCreate
方法,验证了咱们以前对performLaunchActivity的猜测。
总结一下:
在handleLaunchActivity
方法里,会回调如下生命周期:
onCreate()
->onStart()
->onPostCreate()
->onResume()
注意:
若是ActivityClientRecord.startsNotResumed = true
时,生命周期流程将会变为:
onCreate()
->onStart()
->onPostCreate()
->onResume()
->onPause()
经过上节内容的介绍,咱们知道在handleLaunchActivity
方法里,会回调Activity的onCreate()
生命周期方法。
而通常咱们在建立Activity时,会在onCreate()
中调用setContentView
来设置布局文件。
下面,咱们开始分析,咱们自定义的布局文件是如何被加载出来的。
首先分析Activity中的setContentView
方法的源码。
public class Activity extends .... {
// 省略部分代码
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
// 省略部分代码
}
复制代码
根据前面的分析可知,这里的getWindow()
方法返回的是该Activity所特有的Window对象,它在attach
方法中被赋值。
而Window是一个抽象类,经过查看类上的注解可知,它只有一个名为PhoneWindow
的子类,因此咱们直接看PhoneWindow的setContentView方法。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 省略部分代码
ViewGroup mContentParent;
@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 == null
,调用installDecor()
方法,后续将传入的布局资源,加载到mContentParent
中。
因此这里能够确定:installDecor()
方法,主要做用就是为了建立mContentParent
对象。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 省略部分代码
private DecorView mDecor;
ViewGroup mContentParent;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
// 省略部分代码
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// 省略后续设置icon、title等代码
}
}
}
复制代码
嗯,这里确实经过generateLayout
方法建立了mContentParent
对象,但在建立以前,先建立了一个DecorView
对象,并将其做为参数传入generateLayout
方法里。
而DecorView
,咱们只须要知道它继承至FrameLayout
便可,由于此时分析它的细节,对于咱们并没太大帮助。
那咱们分析generateLayout
作了什么:
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 省略部分代码
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 省略部分代码:从主题文件中读取内容,并设置对应的Flag
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
//省略部分代码:经过features,为layoutResource设置不一样布局资源id
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//省略部分代码:为mDecor设置background、elevation、title、titleColor等数据
mDecor.finishChanging();
return contentParent;
}
}
复制代码
主要作了如下内容:
Window
中。mDecor
加载不一样的布局文件。mContentParent
对象(id为com.android.internal.R.id.content
)。这里咱们能够得出如下信息:
Window
加载不一样的布局到DecorView
中。setContentView
方法,实际是将自定义的布局文件,加载到mContentParent
中。至此,咱们能够简单总结一下setContentView
的流程:
一、首先,Activity中的
Window
对象会建立一个DecorView
。
二、而后根据不一样的主题,让DecorView
加载不一样的布局资源。
三、获取这些布局资源中的mContentParent
,它的id为com.android.internal.R.id.content
。
四、最后将自定义的布局加载到mContentParent
中。
在上述setContentView
的流程中,全部的布局资源都已加载完毕,而布局的加载又会涉及到addView
方法。
通常来讲,调用
addView
方法,都会间接调用到requestLayout()
和invalidate(true)
方法,形成界面从新布局和刷新。
而咱们也知道:
Activity在
onCreate
时,界面并不会被加载出来。
这里仿佛出现了矛盾,那咱们再分析分析,看看为何调用了addView
方法后,界面却没有被加载出来。
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
// 省略部分代码
public void addView(View child, int index, LayoutParams params) {
// 省略部分代码
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
// 省略部分代码
}
复制代码
public class View implements ... {
// 省略部分代码
public void requestLayout() {
// 省略部分代码
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
// 省略部分代码
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
// 省略部分代码
if (skipInvalidate()) {
return;
}
// 省略后续代码
}
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
// 省略部分代码
}
复制代码
哦,原来此时DecorView
没有父容器,致使这里只会执行添加操做,而不会去从新布局和刷新。
那何时,界面才会被加载出来呢?
只要学过Android生命周期的人,都知道:
当Activity处于
onCreate
时,是不可见的。
当Activity处于onStart
时,可见,但不可交互。
当Activity处于onResume
时,才是可见可交互的。
那咱们看看Activity
的performStart
方法实现:
public class Activity extends .... {
// 省略部分代码
final void performStart() {
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStart()");
}
mFragments.dispatchStart();
mFragments.reportLoaderStart();
// 省略部分代码
mActivityTransitionState.enterReady(this);
}
复制代码
这里只要调用了Instrumentation
的callActivityOnStart
方法,而callActivityOnStart
方法内部的实现,只是简单调用了传入的Activity对象的onStart()
方法。
emmmmm......好像有点不对啊,performStart方法里好像也没作什么操做啊,难不成界面渲染是在onResume()
里?
带着疑惑,咱们一块儿来看看handleResumeActivity
方法:
public final class ActivityThread {
// 省略部分代码
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
// 省略部分代码
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
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;
// 省略部分代码
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);
}
}
// 省略后续代码
}
}
}
}
复制代码
其中performResumeActivity
也没作过多操做,只是调用了Activity的performResume()
方法,间接调用到onResume
,咱们就不过多分析了。
这里比较核心的是,将DecorView添加到ViewManager
中,这里间接调用到WindowManagerGlobal
的addView
方法,它是一个单例对象,注意这里的断定条件,能够看出,一个ActivityClientRecord
对象,以后执行一次。
public final class WindowManagerGlobal {
// 省略部分代码
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
// 省略部分代码
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// 省略部分代码
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;
}
}
}
// 省略部分代码
}
复制代码
这里先建立ViewRootImpl
对象,而后进行缓存到数组中。接下来把DecorView
设置到ViewRootImpl
中,进而执行到ViewRootImpl
的requestLayout() -> scheduleTraversals() -> doTraversal()
方法,最终执行到performTraversals()
方法中。
而performTraversals()
里,会执行View的绘制流程,包括测量
、摆放
和绘制
三部分,这个咱们后面单独在View渲染章节来说。咱们只需明白,此时界面就已经被渲染出来了。
如今,咱们已经知道了,Activity渲染到屏幕,是在onResume()
以后才完成的,那为啥说,onStart()
是可见但不可交互
的呢? 这里就不卖关子了,其实官方所说的"可见",其实有必定的歧义,我认为:
"可见"只是针对于
已被渲染过
的Activity,而不是正在建立
的Activity。
参照下面这张图来解释一下:
Resumed
时完成的。Resumed
状态切换到Started
状态,界面被部分覆盖,失去焦点,即没法进行交互。Started
状态切换到Created
状态,界面被彻底覆盖,即不可见。Created
状态切换到Started
状态,界面再次被部分覆盖,依然获取不到焦点,没法交互。Started
状态切换到Resumed
状态,界面彻底显示。正是由于这样,才会形成如下这个的问题:
Activity建立过程当中,在
onCreate()
、onStart()
、onResume()
方法里,都没法获取控件的宽高。
本文讲解了Activity启动时,生命周期的调用和界面加载流程,总结起来,整个流程以下: