我们从Activity的setContentView()入手,开始源码解析,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//Activity.setContentView
public void setContentView( int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
//PhoneWindow.setContentView
public void setContentView( int layoutResID) {
if (mContentParent == null ) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
|
发现是使用mLayoutInflater创建View的,所以我们去LayoutInflater.inflate()里面看下,
1
2
3
4
5
6
7
8
9
|
public View inflate( int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println( "INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
|
先根据resource id 获取到XmlResourceParseer,意如其名,就是xml的解析器,继续往下,进入到inflate的核心方法,有些长,我们只分析关键部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
......
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);
}
......
} 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 ;
}
return result;
}
}
|
如果tag的名字不是TAG_1995(名字是个梗),就调用函数createViewFromTag()创建View,进去看看,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
View createViewFromTag(View parent, String name, AttributeSet attrs) {
if (name.equals( "view" )) {
name = attrs.getAttributeValue( null , "class" );
}
......
View view;
if (mFactory2 != null ) view = mFactory2.onCreateView(parent, name, mContext, attrs);
else if (mFactory != null ) view = mFactory.onCreateView(name, mContext, attrs);
else view = null ;
if (view == null && mPrivateFactory != null ) {
view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
}
if (view == null ) {
if (- 1 == name.indexOf( '.' )) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null , attrs);
}
}
if (DEBUG) System.out.println( "Created view is: " + view);
return view;
......
}
|
首先尝试用3个Fractory创建View,如果成功就直接返回了。注意,我们可以利用这个机制,创建自己的Factory来控制View的创建过程。
如果没有Factory或创建失败,那么走默认逻辑。
先判断name中是否有'.'字符,如果没有,则认为使用android自己的View,此时会在name的前面加上包名"android.view.";如果有这个'.',则认为使用的自定义View,这时无需添加任何前缀,认为name已经包含全包名了。
最终,使用这个全包名的name来创建实例,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
new HashMap<String, Constructor<? extends View>>();
protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, "android.view." , attrs);
}
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
Class<? extends View> clazz = null ;
......
if (constructor == null ) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View. class );
if (mFilter != null && clazz != null ) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null ) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null ) {
// New class -- remember whether it is allowed
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View. class );
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
Object[] args = mConstructorArgs;
args[ 1 ] = attrs;
return constructor.newInstance(args);
......
}
|
从源码中看到,在创建实例前,会先从一个静态Map中获取缓存,
1
|
Constructor<? extends View> constructor = sConstructorMap.get(name);
|
缓存的是Constructor对象,目的是用于创建实例,这里要注意sConstructorMap是静态的,并且通过Constructor创建的实例,是使用和Constructor对象同一个ClassLoader来创建的,换句话说,在同一个进程中,同一个自定义View对象,是无法用不同ClassLoader加载的,如果想解决这个问题,就不要让系统使用createView()接口创建View,做法就是自定义Factory或Factory2来自行创建View。
继续往下看,如果缓存里没有,则创建View的Class对象clazz,并缓存到sConstructorMap中,
1
2
3
4
5
6
7
8
9
10
11
12
13
|
if (constructor == null ) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View. class );
if (mFilter != null && clazz != null ) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
}
|
然后就是newInstance了,至此这个View便从xml中变成了java对象,我们继续返回到inflate函数中,看看这个View返回之后做了什么,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
......
// 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 ) {
// 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);
}
}
// Inflate all children under temp
rInflate(parser, temp, attrs, true );
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
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;
}
......
return result;
|
从createViewFromTag返回后,会调用个rInflate(),其中parent参数就是刚才创建出的View,应该能猜到里面做了什么,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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();
}
|
没错,就是递归的使用createViewFromTag()创建子View,并通过ViewGroup.addView添加到parent view中。
之后,这个View树上的所有View都创建完毕。然后会根据inflate()的参数(root和attachToRoot)判断是否将新创建的View添加到root view中,
1
2
3
|
if (root != null && attachToRoot) {
root.addView(temp, params);
}
|
然后,inflate()就将View返回了。
以上内容是小编给大家介绍的android从xml加载到view对象过程解析,希望对大家有所帮助!