LayoutInflater實例化布局流程分析

LayoutInflater通過inflate方法組裝一個指定layout的布局View
<pre>
public void inflate(int resource, ViewGroup root, boolean attachToRoot) {
final Resource res = getContext().getResources();
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
</pre>
整個方法可以分為兩個流程分析错负,第一個部分是由res.getLayout(id)方法獲取指定布局id文件在編譯后的XmlResourceParser對象;第二部分是通過inflate(parser, root, attachToRoot)方法完成組裝View的任務(wù)鹃锈。

首先android加載資源時通過
<pre>
public xmlResourceParser getLayout(int id) {
return loadXmlResourceParser(id, "layout");
}
public XmlResourceParser getAnimation(int id) {
return loadXmlResourceParser(id, "anim");
}
public XmlResourceParser getXml(int id) {
return loadXmlResourceParser(id, "xml");
}
</pre>
這些方法獲取對應(yīng)類型資源的XmlResourceParser對象笋熬。
這些方法都統(tǒng)一調(diào)用了loadXmlResourceParser(id, type)方法
<pre>
XmlResourceParser loadXmlResourceParser(int id, String type) {
......
TypedValue value = mTmpValue;
if (value == null) {
mTmpValue = value = new TypedValue();
}
getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
return loadXmlResourceParser(value.string.toString(), id, value.assetCookie, type);
......
}
</pre>
創(chuàng)建了TypedValue對象较锡,用于存放對應(yīng)的資源信息点楼,并通過getValue方法向TypedValue填充數(shù)據(jù)宠纯。
<pre>
/*
*返回一個指定資源id的相關(guān)資源信息

  • @param id 給定的資源id斗躏,由aapt tool生成膊夹。是由package衬浑、type和resource三部分加密組成。0表示無效id放刨。
  • @param outValue 放置資源數(shù)據(jù)的對象工秩。
  • @param resoveRefs 如果為true,這個資源將會繼續(xù)尋找它所引用的資源进统,直到找到最終的真實資源數(shù)據(jù)助币。如果為false,TypedValue會使用這個引用本身填充螟碎。
    public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException {
    boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
    if (found) {
    return;
    }
    throw new NotFoundException("Resource ID" + Integer.toHexSring(id));
    }
    </pre>
    調(diào)用mAssets的getResourceValue方法眉菱,mAssets就是AssetManager,返回值found表明是否找到了對應(yīng)資源掉分。
    <pre>
    final boolean getResourceValue(int ident, int density, TypedValue outValue, boolean resolveRefs) {
    int block = loadResourceValue(ident, (short) density, outValue, resolvesRefs);
    if (block >= 0) {
    if (outValue.type != TypedValue.TYPE_STRING) {
    return true;
    }
    outValue.string = mStringBlocks[block].get(outValue.data);
    return true;
    }
    return false;
    }
    </pre>
    文件位置:frameworks/base/core/java/android/content/res/AssetManager
    返回值block大于等于0說明找到了對應(yīng)的資源俭缓,并指明了在資源列表中的位置。

通過jni調(diào)用了本地方法loadResourceValue
<pre>
private native final int loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve);
</pre>
通過本地C++方法獲取到指定ident的資源信息并填充到outValue中酥郭。
到此华坦,TypedValue對象已經(jīng)填充了對應(yīng)的資源數(shù)據(jù),然后繼續(xù)前面的流程不从,調(diào)用loadXmlResourceParser(value.string.toString(), id, value.assetCookie, type)方法
<pre>
XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie, String type) {
......
//先看看緩存中是否有需要的bolck對象
final int num = mCachedXmlBlockIds.length;
for (int i=0; i<num; i++) {
if (mCachedXmlBlockIds[i] == id) {
return mCachedXmlBlocks[i].newparser();
}
}
//如果緩存中沒有惜姐,就創(chuàng)建一個新的block并放入緩存中的下一個位置
XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
if (block != null) {
int pos = mLastCachedXmlBlockIndex+1;
if (pos >= num) pos = 0;
mLastCachedXmlBlockIndex = pos;
XmlBlock oldBlock = mCacheXmlBlocks[pos];
if (oldBlock != null) {
oldBlock.close();
}
mCachedXmlBlockIds[pos] = id;
mCachedXmlBlocks[pos] = block;
return block.newParser();
}
......
}
</pre>
mCachedXmlBlockIds是一個長度4的int型數(shù)組,mCachedXmlBlocks是長度4的XmlBlock數(shù)組椿息,它們分別用于緩存最近生成的四個xml布局的id和XmlBlock對象歹袁。mLastCachedXmlBlockIndex表示最后一個緩存位置,當(dāng)大于4時重置為0寝优。明白了這些条舔,上面的代碼邏輯就十分好理解了:循環(huán)查詢mCachedXmlBlockIds是否緩存有請求的id,如果有倡勇,直接返回mCacheXmlBlocks[i].newParser生成的XmlResourceParser對象逞刷,如果沒有則使用AssetManager.openXmlBlockAsset生成一個指定file的XmlBlock對象嘉涌,并放入緩存中,然后返回newParser生成的XmlResourceParser對象夸浅。
xmlBolck是對編譯后的xml文件的一個包裝仑最。這里的AssetManager.openXmlBlockAsset最終也是調(diào)用了本地C++方法獲取了編譯后的資源存放位置信息。

至此帆喇,Resource類通過getLayout方法查找并返回了指定布局id的XmlResourceParser對象警医。之后就通過inflate(parser, root, attachToRoot)方法完成組裝工作了

組裝流程:
<pre>
public void inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
......
View result = root;
try {
......
final String name = parser.getName();
......
if (TAG_MERGE.equals(name)) {
......
rInflate(parser, root, inflaterContext, attrs, false);
} else {
//#1-1
final View temp = createViewFromTag(root, name, inflaterContext, attars);
ViewGroup.LayoutParams params = null;
if (root != null) {
......
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
......
rInflateChildren(parser, temp, attrs, true);
......
//#1-2
if (root != null && attachToRoot) {
root.addView(temp, params);
}
if (root != null || !attachToRoot) {
result = temp;
}
}
} catch.......
......
return result;
}
</pre>
代碼設(shè)置result作為返回對象并初始化為root,在#1-1處通過createViewFromTag方法創(chuàng)建View對象temp坯钦,在#1-2處判斷根root是否為空预皇,不為空說明根布局不為空,就可將temp添加進root婉刀;如果root為空吟温,就給result設(shè)置為temp,最后將result返回突颊。再來看createViewFromTag方法:
<pre>
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}

View createViewFromTag(View parent, String name, Context context, AttributeSet attars, boolean ignoreThemeAttr) {
......
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attars);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attars);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attars);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attars);
} else {
view = createView(name, null, attars);
}
} finally {
mConstructorArgs[0] = lastContext;
}
return view;
......
}
</pre>
首先鲁豪,會依次判斷mFactory2,mFactory和mPrivateFactory這些UI創(chuàng)建工廠是否為空,若不為空律秃,就調(diào)用onCreateView方法創(chuàng)建view爬橡。如果都為空,就進行第二次判斷棒动,判斷節(jié)點的name是否包含'.'糙申,如果包含就是系統(tǒng)view,會調(diào)用本類的onCreateView方法船惨;如果不包含就是自定義view柜裸,會調(diào)用本類的createView方法。
其中onCreateView也就是創(chuàng)建自定義view時其實是先調(diào)用子類的onCreateView的掷漱,原因是LayoutInflater的實現(xiàn)類是PhoneLayoutInflater粘室,這個創(chuàng)建過程后面再說,先來看PhoneLayoutInflater的onCreateView:
<pre>
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};

@Override
protected View onCreateView(String name, AttributeSet attars) {
for(String prefix : sClassPrefixList) {
try {
View view = createView(name, prefix, attars);
if (view != null) {
return view;
}
} catch (ClassNotFoundException e) {
}
}
return super.onCreateView(name, attars);
}
</pre>
經(jīng)過輪詢"android.widget""android.webkit""android.app"卜范,通過父類的createView方法創(chuàng)建view衔统,如果其中一個系統(tǒng)前綴能夠創(chuàng)建出view,就直接返回海雪,如果沒有成功的锦爵,就調(diào)用父類LayoutInflate的onCreateView,在父類里就會直接調(diào)用createView(name, "android.view.", attars)奥裸。這樣一來险掀,最終就都到了createView方法:
<pre>
public final View createView(String name, String prefix, AttributeSet attars) {
......
final View view = constructor.newInstance(args);
......
return view;
......
}
</pre>
方法通過newInstance實例化出相應(yīng)節(jié)點view。

最后湾宙,再來追溯上面提到的子類PhoneLayoutInflater的實現(xiàn)過程樟氢。
LayoutInflater是通過from方法創(chuàng)建實例的:
<pre>
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
</pre>
通過context的getSystemService獲取冈绊,而context的實現(xiàn)類其實是ContextImpl:
<pre>
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
</pre>
然后調(diào)用SystemServiceRegistry的getSystemService:
<pre>
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
</pre>
通過SYSTEM_SERVICE_FETCHERS獲取對應(yīng)的ServiceFetcher對象,而SYSTEM_SERVICE_FETCHERS是通過registerService方法賦值的:
<pre>
private static <T> void registerService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
</pre>
registerService就是設(shè)置各種各樣Manager的地方埠啃,設(shè)置registerService是在本類的靜態(tài)塊中進行的
<pre>
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>({
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
</pre>
在這里就給出了最終將會獲取的LayoutInflater子類:PhoneLayoutInflater死宣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碴开,隨后出現(xiàn)的幾起案子毅该,更是在濱河造成了極大的恐慌,老刑警劉巖潦牛,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眶掌,死亡現(xiàn)場離奇詭異,居然都是意外死亡巴碗,警方通過查閱死者的電腦和手機朴爬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來良价,“玉大人寝殴,你說我怎么就攤上這事∶鞴福” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵市咽,是天一觀的道長痊银。 經(jīng)常有香客問我,道長施绎,這世上最難降的妖魔是什么溯革? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮谷醉,結(jié)果婚禮上致稀,老公的妹妹穿的比我還像新娘。我一直安慰自己俱尼,他們只是感情好抖单,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著遇八,像睡著了一般矛绘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刃永,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天货矮,我揣著相機與錄音,去河邊找鬼斯够。 笑死囚玫,一個胖子當(dāng)著我的面吹牛喧锦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抓督,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼裸违,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了本昏?” 一聲冷哼從身側(cè)響起供汛,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涌穆,沒想到半個月后怔昨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡宿稀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年趁舀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祝沸。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡矮烹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出罩锐,到底是詐尸還是另有隱情奉狈,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布涩惑,位于F島的核電站仁期,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏竭恬。R本人自食惡果不足惜跛蛋,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痊硕。 院中可真熱鬧赊级,春花似錦、人聲如沸岔绸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亭螟。三九已至挡鞍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間预烙,已是汗流浹背墨微。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扁掸,地道東北人翘县。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓最域,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锈麸。 傳聞我的和親對象是個殘疾皇子镀脂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)忘伞,斷路器薄翅,智...
    卡卡羅2017閱讀 134,702評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法氓奈,內(nèi)部類的語法翘魄,繼承相關(guān)的語法,異常的語法舀奶,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • #深度理解Android InstantRun原理以及源碼分析 @Author 莫川 ##Instant Run官...
    鄭海波mobctrl閱讀 1,552評論 2 7
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,814評論 0 11
  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗暑竟。 張土汪:刷leetcod...
    土汪閱讀 12,748評論 0 33