注:本文分析的源碼版本爲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啓動時,生命週期的調用和界面加載流程,總結起來,整個流程以下: