Android获取到inflate服务总共有三种方式:
1、使用getSystemService(Context.LAYOUT_INFLATER_SERVICE)
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mInflater.inflate(R.layout.main,parent, false);
2、使用LayoutInflater的静态方法
mInflater = (LayoutInflater)LayoutInflater.from(context);
mInflater.inflate(R.layout.main,parent, false);
3、最简单的一种,直接使用View的inflate
View.inflate(context, R.layout.main, null);
下面看看它们直接的关系:
首先看看第二种方式的源码实现:
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;
}
可以清楚的看到它的本质就是第一种方式。
接着看看第三种方式的源码实现:
public static View inflate(Context context, int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
可以看到它的本质就是第二种方式,而第二种方式的本质是第一种方式,只是它们直接层层进行了封装而已。
下面主要看看两点:
1、context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)是怎样得到的。
关于这个可以看看这篇文章:浅析getSystemService获取系统服务
2、mInflater.inflate(R.layout.main,parent, false)的工作过程。
下面我们就来看看这个函数的工作过程,直接进入inflate函数,它在LayoutInflater里面。
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
// 1、根据指定的id读取xml布局文件,最终返回一个Xml资源解析器
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
// 2、对这个布局文件进行解析
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
下面我们来看看上面两步里面的内容:
(1)getContext().getResources().getLayout(resource)
我们直接进入Resources类里面的getLayout方法:
public XmlResourceParser getLayout(int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
/*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
throws NotFoundException {
synchronized (mAccessLock) {
TypedValue value = mTmpValue;
if (value == null) {
mTmpValue = value = new TypedValue();
}
// 1、得到指定ID的xml资源,输出到value中
getValue(id, value, true);
// 2、如果value的类型为String类型,调用loadXmlResourceParser进行解析
if (value.type == TypedValue.TYPE_STRING) {
return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException(
"Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ Integer.toHexString(value.type) + " is not valid");
}
}
我们主要来看看第二步,因为一般情况上面都为true:
/*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
int assetCookie, String type) throws NotFoundException {
if (id != 0) {
try {
// These may be compiled...
synchronized (mCachedXmlBlockIds) {
// 首先看这个block是否在缓存中
// 如果在缓存中,就直接返回它的parser
final int num = mCachedXmlBlockIds.length;
for (int i=0; i<num; i++) {
if (mCachedXmlBlockIds[i] == id) {
//System.out.println("**** REUSING XML BLOCK! id="
// + id + ", index=" + i);
return mCachedXmlBlocks[i].newParser();
}
}
// 如果不在缓存中,则需要创建一个,并且将它缓存起来
// 最终返回一个XmlPullParser
XmlBlock block = mAssets.openXmlBlockAsset(
assetCookie, file);
if (block != null) {
int pos = mLastCachedXmlBlockIndex+1;
if (pos >= num) pos = 0;
mLastCachedXmlBlockIndex = pos;
XmlBlock oldBlock = mCachedXmlBlocks[pos];
if (oldBlock != null) {
oldBlock.close();
}
mCachedXmlBlockIds[pos] = id;
mCachedXmlBlocks[pos] = block;
//System.out.println("**** CACHING NEW XML BLOCK! id="
// + id + ", index=" + pos);
return block.newParser();
}
}
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
"File " + file + " from xml type " + type + " resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
}
throw new NotFoundException(
"File " + file + " from xml type " + type + " resource ID #0x"
+ Integer.toHexString(id));
}
其实的过程其实就是得到xml布局文件的Pull解析器,LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的。下面我们来看看整个xml文件的解析过程。
(2)inflate(parser, root, attachToRoot)
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, attrs, false);
} else {
// Temp is the root view that was found in the xml
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp
rInflate(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// 如果它的父布局不为null,并且attachToRoot为true
//这样就会把整个布局文件加到父布局中去
//从这里也可以看到inflate的第三个参数attachToRoot的作用
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
使用过Pull解析器的应该对这个过程比较熟悉。在上面它会解析每一个节点,基本过程如下:
1、判断最外层的是否为merge标签,如果是,则直接使用rInflate来解析里面的所有子元素。
2、如果最外层不是merger标签,通过createViewFromTag()这个方法,并把节点名和参数传了进去,节点名来创建View对象,这个就是最外层的布局对象。
3、创建了最外层的布局对象,接着通过rInflate来解析它里面的所以子元素。
下面我们可以看看它解析子元素的过程,看看rInflate源码。
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else if (TAG_1995.equals(name)) {
final View view = new BlinkLayout(mContext, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
} else {
final View view = createViewFromTag(parent, name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (finishInflate) parent.onFinishInflate();
}
从这里我们可以看到,它就是一步一步递归来对这个布局文件的各个标签进行解析,根据标签的名字来进行不同的操作,所以他上面进行了一系列的判断,对应的会创建不同的View对象,然后把它们加入到父布局中。这样的话,把整个布局文件都解析完成后就形成了一个完整的DOM结构,最终会把最顶层的根布局返回,至此inflate()过程全部结束。