android xml view对象,Android从xml加载到View对象过程解析

我们从Activity的setContentView()入手,开始源码解析,

//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()里面看下,

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的核心方法,有些长,我们只分析关键部分:

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {

......

if (TAG_MERGE.equals(name)) {

if (root == null || !attachToRoot) {

throw new InflateException(" 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,进去看看,

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来创建实例,

private static final HashMap> sConstructorMap =

new HashMap>();

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中获取缓存,

Constructor extends View> constructor = sConstructorMap.get(name);

缓存的是Constructor对象,目的是用于创建实例,这里要注意sConstructorMap是静态的,并且通过Constructor创建的实例,是使用和Constructor对象同一个ClassLoader来创建的,换句话说,在同一个进程中,同一个自定义View对象,是无法用不同ClassLoader加载的,如果想解决这个问题,就不要让系统使用createView()接口创建View,做法就是自定义Factory或Factory2来自行创建View。

继续往下看,如果缓存里没有,则创建View的Class对象clazz,并缓存到sConstructorMap中,

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返回之后做了什么,

......

// 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,应该能猜到里面做了什么,

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(" cannot be the root element");

}

parseInclude(parser, parent, attrs);

} else if (TAG_MERGE.equals(name)) {

throw new InflateException(" 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中,

if (root != null && attachToRoot) {

root.addView(temp, params);

}

然后,inflate()就将View返回了。

以上内容是小编给大家介绍的android从xml加载到view对象过程解析,希望对大家有所帮助!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值