源碼版本:Android 27
一、應(yīng)用的啟動
首先我們需要知道:
ActivityThread
的main
方法梳侨,是Android應(yīng)用程序啟動時的入口點。
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方法里執(zhí)行了以下操作:
- 1、首先調(diào)用了
Looper.prepareMainLooper()
方法铅协,它會創(chuàng)建一個與當前線程(主線程)相關(guān)聯(lián)的Looper
對象匠楚。 - 2巍膘、然后創(chuàng)建一個
ActivityThread
對象,并調(diào)用其attach()
方法芋簿。 - 3峡懈、最后調(diào)用
Looper.loop()
方法,讓剛創(chuàng)建的Looper
對象与斤,從它的MessageQueue
中循環(huán)讀取數(shù)據(jù)并執(zhí)行肪康。
現(xiàn)在,我們開始分析ActivityThread
的attach()
方法做了什么撩穿?
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();
}
// 省略后續(xù)代碼
}
}
}
在attach
方法中磷支,調(diào)用ActivityManager.getService()
方法,獲取到遠程的IActivityManager
對象食寡,并將一個ApplicationThread
實例傳入雾狈。
ApplicationThread
繼承至IApplicationThread.Stub
,即Binder
進程間通信的本地實現(xiàn)類冻河。它有很多與Activity生命周期相關(guān)的方法箍邮,大都以schedule
開頭:
至此茉帅,我們可以總結(jié)一下:
1、
ActivityThread
的main
方法锭弊,是應(yīng)用程序啟動的入口堪澎。
2、Activity生命周期是由系統(tǒng)底層控制味滞,通過Binder
機制樱蛤,回調(diào)到ApplicationThread
的scheduleXXX
方法中。
3剑鞍、ActivityThread
和ApplicationThread
針對每個應(yīng)用昨凡,都只有一個實例。
這里有個問題:
為什么沒看到scheduleStartActivity
方法蚁署?難道Activity的onStart()
生命周期回調(diào)便脊,不是由ActivityManager
發(fā)送消息控制的?
帶著這個疑問光戈,我們開始閱讀這些scheduleXXX
方法的源碼哪痰。
二、Activity生命周期
我們從ApplicationThread
中的scheduleLaunchActivity
方法開始分析久妆,因為由名字可以猜測它應(yīng)該會執(zhí)行Activity創(chuàng)建和啟動工作晌杰。
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
方法中,首先創(chuàng)建了一個ActivityClientRecord
對象筷弦,并對其進行賦值肋演。然后通過Handler,將線程由Binder線程
切換到主線程
烂琴。最終調(diào)用到ActivityThread
的handleLaunchActivity
方法爹殊。
注意:
ActivityClientRecord
主要用于記錄與Activity相關(guān)的數(shù)據(jù)。
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
方法中做了什么便斥,僅分析后續(xù)代碼捧请。后續(xù)代碼中,調(diào)用了handleResumeActivity
,猜測它應(yīng)該會調(diào)用Activity的onResume
方法殖熟。
根據(jù)Activity生命周期推測到:
在
performLaunchActivity
方法里胀莹,一定會依次調(diào)用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相關(guān)代碼 */ }
return activity;
}
上述代碼主要執(zhí)行了以下操作:
- 1该酗、創(chuàng)建Activity對象
調(diào)用Instrumentation
的newActivity
方法,通過反射創(chuàng)建Activity
對象。
- 2呜魄、初始化Activity
調(diào)用Activity對象的attach
方法悔叽,用于初始化Activity的一些數(shù)據(jù),同時會為Activity設(shè)置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();
// 省略部分代碼
}
// 省略部分代碼
}
- 3趟庄、調(diào)用3個生命周期方法
1、
Instrumentation.callActivityOnCreate
方法伪很,該方法中會調(diào)用activity.performCreate()
方法戚啥。
2、activity.performStart()
方法锉试。
3猫十、Instrumentation.callActivityOnPostCreate
方法,該方法中會調(diào)用activity.onPostCreate()
方法键痛。
查看里面的源碼炫彩,確實依次調(diào)用了onCreate、onStart絮短、onPostCreate方法,驗證了我們之前對performLaunchActivity
的猜想昨忆。
總結(jié)一下:
在handleLaunchActivity
方法里丁频,會回調(diào)以下生命周期:
onCreate()
->onStart()
->onPostCreate()
->onResume()
注意:
如果ActivityClientRecord.startsNotResumed = true
時,生命周期流程將會變?yōu)椋?br>
onCreate()
-> onStart()
-> onPostCreate()
-> onResume()
-> onPause()
二邑贴、界面加載
通過上節(jié)內(nèi)容的介紹席里,我們知道在handleLaunchActivity
方法里,會回調(diào)Activity的onCreate()
生命周期方法拢驾。
而一般我們在創(chuàng)建Activity時奖磁,會在onCreate()
中調(diào)用setContentView
來設(shè)置布局文件。
下面繁疤,我們開始分析咖为,我們自定義的布局文件是如何被加載出來的。
2.1 設(shè)置布局
首先分析Activity中的setContentView
方法的源碼稠腊。
public class Activity extends .... {
// 省略部分代碼
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
// 省略部分代碼
}
根據(jù)前面的分析可知躁染,這里的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
,調(diào)用installDecor()
方法埋嵌,后續(xù)將傳入的布局資源破加,加載到mContentParent
中。
所以這里可以肯定:installDecor()
方法莉恼,主要作用就是為了創(chuàng)建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);
// 省略后續(xù)設(shè)置icon、title等代碼
}
}
}
嗯俐银,這里確實通過generateLayout
方法創(chuàng)建了mContentParent
對象尿背,但在創(chuàng)建之前,先創(chuàng)建了一個DecorView
對象捶惜,并將其作為參數(shù)傳入generateLayout
方法里田藐。
而DecorView
,我們只需要知道它繼承至FrameLayout
即可吱七,因為此時分析它的細節(jié)汽久,對于我們并沒太大幫助。
那我們分析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.
// 省略部分代碼:從主題文件中讀取內(nèi)容踊餐,并設(shè)置對應(yīng)的Flag
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
//省略部分代碼:通過features景醇,為layoutResource設(shè)置不同布局資源id
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//省略部分代碼:為mDecor設(shè)置background、elevation吝岭、title三痰、titleColor等數(shù)據(jù)
mDecor.finishChanging();
return contentParent;
}
}
主要做了以下內(nèi)容:
- 1、從主題中獲取數(shù)據(jù)窜管,并應(yīng)用到當前
Window
中散劫。 - 2、根據(jù)features幕帆,讓
mDecor
加載不同的布局文件获搏。 - 3、獲得
mContentParent
對象(id為com.android.internal.R.id.content
)失乾。
這里我們可以得出以下信息:
- 1常熙、不同的主題會讓
Window
加載不同的布局到DecorView
中。 - 2仗扬、
setContentView
方法症概,實際是將自定義的布局文件,加載到mContentParent
中早芭。
至此彼城,我們可以簡單總結(jié)一下setContentView
的流程:
1、首先,Activity中的
Window
對象會創(chuàng)建一個DecorView
募壕。
2调炬、然后根據(jù)不同的主題,讓DecorView
加載不同的布局資源舱馅。
3缰泡、獲取這些布局資源中的mContentParent
,它的id為com.android.internal.R.id.content
代嗤。
4棘钞、最后將自定義的布局加載到mContentParent
中。
2.2 渲染布局
在上述setContentView
的流程中干毅,所有的布局資源都已加載完畢宜猜,而布局的加載又會涉及到addView
方法。
一般來說硝逢,調(diào)用
addView
方法姨拥,都會間接調(diào)用到requestLayout()
和invalidate(true)
方法,造成界面重新布局和刷新渠鸽。
而我們也知道:
Activity在
onCreate
時叫乌,界面并不會被加載出來。
這里仿佛出現(xiàn)了矛盾徽缚,那我們再分析分析憨奸,看看為什么調(diào)用了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;
}
// 省略后續(xù)代碼
}
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
// 省略部分代碼
}
哦膀藐,原來此時DecorView
沒有父容器,導致這里只會執(zhí)行添加操作红省,而不會去重新布局和刷新。
那什么時候国觉,界面才會被加載出來呢吧恃?
只要學過Android生命周期的人,都知道:
當Activity處于
onCreate
時麻诀,是不可見的痕寓。
當Activity處于onStart
時,可見蝇闭,但不可交互呻率,
當Activity處于onResume
時,才是可見可交互的呻引。
那我們看看Activity
的performStart
方法實現(xiàn):
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);
}
這里只要調(diào)用了Instrumentation
的callActivityOnStart
方法礼仗,而callActivityOnStart
方法內(nèi)部的實現(xiàn),只是簡單調(diào)用了傳入的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);
}
}
// 省略后續(xù)代碼
}
}
}
}
其中performResumeActivity
也沒做過多操作沪羔,只是調(diào)用了Activity的performResume()
方法,間接調(diào)用到onResume
象浑,我們就不過多分析了蔫饰。
這里比較核心的是,將DecorView添加到ViewManager
中愉豺,這里間接調(diào)用到WindowManagerGlobal
的addView
方法篓吁,它是一個單例對象,注意這里的判定條件粒氧,可以看出越除,一個ActivityClientRecord
對象,之后執(zhí)行一次外盯。
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;
}
}
}
// 省略部分代碼
}
這里先創(chuàng)建ViewRootImpl
對象摘盆,然后進行緩存到數(shù)組中。接下來把DecorView
設(shè)置到ViewRootImpl
中饱苟,進而執(zhí)行到ViewRootImpl
的requestLayout() -> scheduleTraversals() -> doTraversal()
方法孩擂,最終執(zhí)行到performTraversals()
方法中,流程圖如下:
View的繪制流程箱熬,會涉及到測量
类垦、擺放
和繪制
三部分,這個我們后面單獨在View渲染章節(jié)來講城须。我們只需明白蚤认,此時界面就已經(jīng)被渲染出來了。
2.3糕伐、延伸
現(xiàn)在砰琢,我們已經(jīng)知道了,Activity渲染到屏幕良瞧,是在onResume()
之后才完成的陪汽,那為啥說,onStart()
是可見但不可交互
的呢褥蚯?
這里就不賣關(guān)子了挚冤,其實官方所說的"可見",其實有一定的歧義赞庶,我認為:
"可見"只是針對于
已被渲染過
的Activity训挡,而不是正在創(chuàng)建
的Activity澳骤。
參照下面這張圖來解釋一下:
1、對于創(chuàng)建的Activity舍哄,只是簡單的走了生命周期宴凉,渲染是在
Resumed
時完成的。
2表悬、對于已被渲染過的Activity:1弥锄、當它由
Resumed
狀態(tài)切換到Started
狀態(tài),界面被部分覆蓋蟆沫,失去焦點籽暇,即無法進行交互。
2饭庞、當它由Started
狀態(tài)切換到Created
狀態(tài)戒悠,界面被完全覆蓋,即不可見舟山。
3绸狐、當它由Created
狀態(tài)切換到Started
狀態(tài),界面再次被部分覆蓋累盗,依然獲取不到焦點寒矿,無法交互。
4若债、當它由Started
狀態(tài)切換到Resumed
狀態(tài)符相,界面完全顯示。
正是因為這樣蠢琳,才會造成以下這個的問題:
Activity創(chuàng)建過程中啊终,在
onCreate()
、onStart()
傲须、onResume()
方法里蓝牲,都無法獲取控件的寬高。