欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-24 20:42:40
我们从activity的setcontentview()入手,开始源码解析, //activity.setcontentview public void s...

我们从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对象过程解析,希望对大家有所帮助!