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死宣。