做爲 Android 四大組件之一的 Activity 個人印象中就是用來展現界面的,在很長一段時間裏,只要說起界面、UI、View 我腦子裏第一個閃過的就是 Activity ,個人理解中一直認爲 界面、UI、View == Activity
。其實呢非也!若是你曾經和我同樣,提及 Actvity 就是聊他的生命週期,各個方法背的倒背如流,但也僅限於此而已並無真正的去認識 Activity 的話,那麼請跟我一塊兒從【 Activity 的組成】、【啓動】、【顯示】來從新認識一下 Activityhtml
首先明確一下 Activity 的基本概念,咱們先來看下 Google 官方對 Activity 給出的定義:java
Activity 是一個應用組件,用戶可與其提供的屏幕進行交互,以執行撥打電話、拍攝照片、發送電子郵件或查看地圖等操做。 每一個 Activity 都會得到一個用於繪製其用戶界面的窗口。窗口一般會充滿屏幕,但也可小於屏幕並浮動在其餘窗口之上。android
讀完了好像也沒整明白。架構
咱們拋開以前對 Activity 的印象,從代碼層面上來看他就是一個普通的 Java 類,本質上並非一個 View 或 ViewGroup,他沒有繼承任何 View 和 ViewGroup 相關類。app
從源碼中能夠看到 Activity 實現了 Window 、KeyEvent 等一系列的 Callback 以及 Listener 。看到這些大概能夠知道 Activity 的職責至關於一個控制器了,接收各類回調以及處理監聽事件。ide
基於 Google 官方的定義和 Activity 源碼,Activity 的大概職責能夠總結成一張圖:oop
控制器
角色,負責控制生命週期和事件處理。是時候來揭開 Activity 的廬山真面目了。一圖勝千言!先來看一張圖。佈局
上圖描述了 Activity 從外到內大概的一個層級結構post
頂層 View
當前 Activity 全部 View 的祖先, 即當前 Activity 視圖樹根節點。ui
DecorView 本質上是一個 FrameLayout ( DecorView 類繼承自 FrameLayout )。
其內部包含一個豎直佈局的 LinearLayout ,分爲2部分:
id 爲
title_container
的 TitleBar 。
id 爲
content
的 Content 。在Activity中經過 setContentView()所設置的佈局文件最終就是被添加到此處的 Content 中。
Content 內部的視圖樹對應的就是 Activity 的佈局文件。
視圖承載器
承載視圖 View 的顯示。
PhoneWindow 爲 Window 的實現類。每一個 Activity 均會建立一個 Window 用以承載 View ,是視圖真正的控制者。
鏈接器
WindowManager
和 DecorView
的紐帶。measure
、layout
、draw
。對應實現類爲 ViewRootImpl ,實現了 ViewParent 接口。做爲 WindowManagerGlobal 大部份內部實現的實際實現者,需負責與 WindowManagerService 交互通訊,以調整窗口的位置大小,以及對來自 WindowManagerService 的事件(如窗口尺寸改變等)做出相應的處理。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
......
ViewRootImpl root;
......
root = new ViewRootImpl(view.getContext(), display);
try {
//最終實現,調用 ViewRootImpl setView 方法。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
......
}
}
複製代碼
public ViewRootImpl(Context context, Display display) {
......
mWindowSession = WindowManagerGlobal.getWindowSession();
......
}
//與 WindowManagerService 創建遠程鏈接
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
複製代碼
在簡單分析了 Activity 各個組成元件以後,接下來就是看這些元件是如何完成完整的 Activity 組裝並將 View 呈現到用戶面前。
一圖勝千言:
下面跟隨上圖的流程走一遍源碼(爲避免篇幅過長只展現重點代碼)。
這部分主要作的是一些初始化設置的工做,爲 Actvity 最終呈如今用戶面前作準備。
作爲 Android 應用程序的入口類,先來看下 ActivityTread 作了什麼工做。
源碼路徑:ActivityTread 類 main 方法。
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"));
}
.......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
方法解析:
ActivityTread 的 main 方法初始化了一個 Looper消息循環用以接收主線程的操做消息。
關於Looper消息循環詳細介紹能夠查看 Android Handler淺析一文,這裏就再也不展開。
Application 啓動後,主線程會收到 LAUNCH_ACTIVITY
消息,該消息由 ActivityTread 內部類 H (繼承自 Handler)
接收。
源碼路徑:ActivityTread/H 類 handleMessage 方法。
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
//重點看這個方法
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
}
複製代碼
接收消息後會調用 handleLaunchActivity()
方法,下面重點都在 handleLaunchActivity()
方法裏面了。
源碼路徑:ActivityTread 類 handleLaunchActivity 方法。
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);
}
......
}
複製代碼
方法解析:
次方法內部重點看 performLaunchActivity()
和 handleResumeActivity()
方法的調用。
先來看 performLaunchActivity()
方法,此方法就開始了咱們上圖的第 3~9
步的流程,直到流程走完,方法調用棧會回退至 handleLaunchActivity()
中繼續往下執行,調用 handleResumeActivity()
。
源碼路徑:ActivityTread 類 performLaunchActivity 方法。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
.......
Activity activity = null;
try {
//經過 Activity 類名構建 Actvity 對象
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
} catch (Exception e) {
.......
}
try {
......
if (activity != null) {
......
//爲當前 Activity 初始一個 Window 並設置 WindowManager
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);
......
//經過 Instrumentation 調用 Activity 的 onCreate 方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
......
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
......
}
return activity;
}
複製代碼
方法解析:
performLaunchActivity
方法作了三件重要的事:
activity.attach
方法爲當前 Activity 初始化一個 Window 對象。performLaunchActivity
方法中調用 activity.attach
方法爲當前 Activity 初始化一個 Window 對象,咱們來看下 activity.attach
方法的具體實現。
源碼路徑:Activity 類 attach 方法。
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) {
......
//爲 mWindow 變量賦值,實例化一個 PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
......
//爲 mWindow 設置 WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
......
//爲 mWindowManager 變量賦值
mWindowManager = mWindow.getWindowManager();
.......
}
複製代碼
attach
方法中爲 Activity 的 mWindow 變量實例化了一個 PhoneWindow 對象,PhoneWindow 是 Window 的一個具體實現,這在上文中已經說過,就再也不贅述了。
performLaunchActivity
方法中經過 Instrumentation 調用 Activity 的 onCreate 方法,該方法的具體實如今 Activity 中。 通常來講咱們都會重寫 Activity 的 onCreate 方法並在裏面調用 setContentView
。下面來看下咱們熟悉的 setContentView
裏面作了些什麼。
源碼路徑:Activity 類 setContentView 方法。
public void setContentView(@LayoutRes int layoutResID) {
//調用 PhoneWindow 的 setContentView
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
Activity 的 setContentView
實際的實現來自 PhoneWindow,能夠看下 getWindow()
方法返回的其實就是步驟5
裏 attach
方法賦值的 mWindow
對象。
源碼路徑:Activity 類 getWindow 方法。
public Window getWindow() {
return mWindow;
}
複製代碼
既然這樣咱們不妨跟進 PhoneWindow 中一探究竟:
源碼路徑:PhoneWindow 類 setContentView 方法。
@Override
public void setContentView(int layoutResID) {
// 如mContentParent爲空,建立一個DecroView
if (mContentParent == null) {
//初始化 DecroView
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 {
//爲 mContentParent 添加布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Window.Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
//內容改變回調
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
複製代碼
方法解析:
mContentParent
這個變量就是咱們上文在介紹 DecroView
時提到的 FrameLayout 對應的佈局部分。installDecor()
方法爲 PhoneWindow 初始化一個 DecroView 對象。mContentParent
中。PhoneWindow 的 setContentView 方法會爲當前的 Window 初始化一個 DecorView。
源碼路徑:PhoneWindow 類 installDecor 方法。
private void installDecor() {
if (mDecor == null) {
// 建立 DecorView 對象
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) {
// 爲 DecorView 的 content 設置佈局格式
mContentParent = generateLayout(mDecor);
......
}
}
複製代碼
方法解析:
1.調用 generateDecor
方法建立 DecorView 對象,generateDecor
實現很簡單直接。
源碼路徑:PhoneWindow 類 generateDecor 方法。
protected DecorView generateDecor(int featureId) {
......
return new DecorView(context, featureId, this, getAttributes());
}
複製代碼
generateLayout
方法爲 DecorView 的 content 設置佈局格式。源碼路徑:PhoneWindow 類 generateLayout 方法。
protected ViewGroup generateLayout(DecorView decor) {
//獲取窗口樣式信息
TypedArray a = getWindowStyle();
......
// Inflate the window decor.
//根據主題樣式,加載窗口布局
int layoutResource;
int features = getLocalFeatures();
......
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 獲取 Decorview 對應的 content 的 FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
......
mDecor.finishChanging();
return contentParent;
}
複製代碼
這個步驟的源碼步驟7
中已經貼出來了,在 setContentView
方法中:
//爲 mContentParent 添加布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);
複製代碼
到這裏 handleLaunchActivity
方法中的 performLaunchActivity
方法這條線就差很少走完了,如今回到 handleLaunchActivity
方法中繼續往下執行,下面的關鍵方法是 handleResumeActivity
。
先看 handleResumeActivity
方法的重點代碼。
源碼路徑:ActivityThread 類 handleResumeActivity 方法。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
......
// TODO Push resumeArgs into the activity for consideration
// 調用Activity的onResume()
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
.......
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//添加 DecorView 到 Window
wm.addView(decor, l);
}
......
}
......
}
}
複製代碼
handleResumeActivity
方法中經過調用 performResumeActivity
方法來執行 Activity onResume() 方法。具體實現有興趣的能夠本身跟進源碼進去看下,這裏就再也不貼源碼了,文章篇幅已經長的頭皮發麻了。
handleResumeActivity
方法中經過調用 WindowManager 的 addView
方法將以前流程中已賦值的 DecorView 對象添加至當前 Window。
初始化 ViewRootImpl 工做就是在 WindowManager 的 addView
方法中進行的。而 WindowManager 是一個接口,對應的實現類是 WindowManagerImpl
,那就去 WindowManagerImpl 中找 addView
的具體實現咯。
源碼路徑:WindowManagerImpl 類 addView 方法。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
複製代碼
方法解析:
該方法內部直接調用 WindowManagerGlobal
的 addView
方法,WindowManager 的 addView
方法由 WindowManagerGlobal 代理了。
源碼路徑:WindowManagerGlobal 類 addView 方法。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
.......
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
//建立ViewRootImpl對象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//WindowManager將DecorView實例對象交給ViewRootImpl 繪製View
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
......
throw e;
}
}
}
複製代碼
WindowManagerGlobal 類 addView 方法在建立 ViewRootImpl 對象後調用其 setView
方法將將 DecorView 實例對象交給 ViewRootImpl 用以繪製 View 。
全部一切準備就緒以後咱們終於開始對咱們 Activity 的佈局進行繪製了。最終將調用 ViewRootImpl 的 performTraversals
方法(此方法賊長)開始 View 繪製的三大流程,measure、layout、draw
。
源碼路徑:ViewRootImpl 類 performTraversals 方法。
private void performTraversals() {
...
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
//執行測量流程
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//執行佈局流程
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
//執行繪製流程
performDraw();
}
複製代碼
OK到此 Activity 的部件組裝以及顯示流程就梳理了一個大概,文章篇幅有點長,可是內容不算多,貼了不少代碼,有興趣的能夠跟着個人流程本身跟進源碼看下加深印象。
參考文獻: