版本:27.1.1
觀看目錄:
- Activity:啟動流程
- Activity:xml如何通過加載到UI界面
結合 Activity:啟動流程
的流程圖:
Activity:啟動流程
如果對Activity的啟動流程不熟悉的請移步:Activity:啟動流程
下圖是從
Activity啟動
到
XML如何加載到UI界面
的:
圖有點大,可以點選圖檔放大檢視:
建議跟着源碼過一遍流程圖熟悉下,在看更容易了解!
本文章分析流程圖
截選自上圖後半部分。
本文分析分三個方面來說:
- setContentView為XML加載到UI做了哪些準備工作?
- setContentView之後Activity如何開始運轉工作的?
- 從開始運轉工作到測量繪制布局做了哪些工作?
setContentView
源碼:PhoneWindow中的setContentView
// 隻保留兩個核心方法
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
// 初始化基礎布局
installDecor();
}
// ...省略n行代碼
// Activity的初始布局中的FramLayout,已經完成xml布局的添加
mLayoutInflater.inflate(layoutResID, mContentParent);
// ...省略n行代碼
}
installDecor()
初始化一個基礎布局:擷取DecorView、mContentParent
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
// ... 省略N行代碼
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// ... 省略N行代碼
}
}
// 初始化的基礎布局也是一個XML檔案
protected ViewGroup generateLayout(DecorView decor) {
// ...省略N行代碼
//通過findViewById在基礎布局中找到ViewGroup
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
// ...省略N行代碼
return contentParent;
}
mLayoutInflater.inflate()
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
// ....省略N行代碼
// 擷取XmlResourceParser,用于解析XML
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
// ...省略N行代碼
// Temp is the root view that was found in the xml
// 找到XMl根視圖
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
// ...省略N行代碼
// to root. Do that now.
if (root != null && attachToRoot) {
// addView
root.addView(temp, params);
}
return result;
}
}
到這裡完成了第一步:
setContentView為XML加載到UI做了哪些準備工作?
setContentView之後Activity如何開始運轉工作的?
在
ActivityThread
中調用
performLaunchActivity()
會完成
Activity的啟動
以及
setContentView設定
,會得到Activity對象。
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
// ....省略
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
// 無用代碼不再貼出
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
//...省略N行
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);
}
從開始運轉工作到測量繪制布局做了哪些工作?
這裡都是代碼的跳轉不在一一貼源碼,直接看圖即可。
ActivityThread類:
handleResumeActivity()
=>
WindowManagerImpl類:
addView()
=>
WindowManagerGlobal類:
addView()
=>
ViewRootImpl類:
setView()
=>
這裡會觸發啟動,将參數和視圖交給ViewRootlmpl
ViewRootImpl類:
requestLayout()
=>
scheduleTraversals()
=>
doTraversal()
=>
performTraversals()
開始真正的
測量、布局、繪制
補充:
view.assignParent(this);
給View配置設定Parent,這裡傳的this,說明view的parent就是ViewRootIml
這裡就是吧自己配置設定給自己的子View.這也是mParent的由來
總結
- 在Activity建立後,調用setContentView設定XML,主要加載了一個基礎布局(基礎布局也是一個XML),
完成頂層DecorView、和mContentParent容器的初始化
- 後調用inflate(),
通過XmlResourceParser去解析設定的XML
- 通過
找到布局根視圖,将其添加到createViewFromTag
容器中mContentParent
- 然後handleResumeActivity中擷取
,調用ViewManager
開始Activity的運轉addView
- 層層調用,會在WindowManagerGlobal類中調用ViewRootImpl中的
setView觸發啟動
- setView中調用
,最終會調到requestLayout()
performTraversals()
- 最後performTraversals()
完成最後的=> performMeasure()=> performLayout()=>performDraw()
測量、布局、繪制
到這裡就已經完成
xml如何通過加載到UI界面
後續會繼續分析,是如何測量、布局、繪制。
Thanks!