当前位置: 移动技术网 > IT编程>移动开发>Android > Android从xml加载到View对象过程解析

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

2019年07月24日  | 移动技术网IT编程  | 我要评论

oxco150002,我们结婚了20130615,又名南平站长网

我们从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("<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,进去看看,

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

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

if (root != null && attachtoroot) {
root.addview(temp, params);
}

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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网