Android从xml加载到View对象过程解析
我们从Activity的setContentView()入手,开始源码解析,
//Activity.setContentView
publicvoidsetContentView(intlayoutResID){
getWindow().setContentView(layoutResID);
initActionBar();
}
//PhoneWindow.setContentView
publicvoidsetContentView(intlayoutResID){
if(mContentParent==null){
installDecor();
}else{
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID,mContentParent);
finalCallbackcb=getCallback();
if(cb!=null&&!isDestroyed()){
cb.onContentChanged();
}
}
发现是使用mLayoutInflater创建View的,所以我们去LayoutInflater.inflate()里面看下,
publicViewinflate(intresource,ViewGrouproot,booleanattachToRoot){
if(DEBUG)System.out.println("INFLATINGfromresource:"+resource);
XmlResourceParserparser=getContext().getResources().getLayout(resource);
try{
returninflate(parser,root,attachToRoot);
}finally{
parser.close();
}
}
先根据resourceid获取到XmlResourceParseer,意如其名,就是xml的解析器,继续往下,进入到inflate的核心方法,有些长,我们只分析关键部分:
publicViewinflate(XmlPullParserparser,ViewGrouproot,booleanattachToRoot){
......
if(TAG_MERGE.equals(name)){
if(root==null||!attachToRoot){
thrownewInflateException("<merge/>canbeusedonlywithavalid"
+"ViewGrouprootandattachToRoot=true");
}
rInflate(parser,root,attrs,false);
}else{
//Tempistherootviewthatwasfoundinthexml
Viewtemp;
if(TAG_1995.equals(name)){
temp=newBlinkLayout(mContext,attrs);
}else{
temp=createViewFromTag(root,name,attrs);
}
......
}catch(XmlPullParserExceptione){
InflateExceptionex=newInflateException(e.getMessage());
ex.initCause(e);
throwex;
}catch(IOExceptione){
InflateExceptionex=newInflateException(
parser.getPositionDescription()
+":"+e.getMessage());
ex.initCause(e);
throwex;
}finally{
//Don'tretainstaticreferenceoncontext.
mConstructorArgs[0]=lastContext;
mConstructorArgs[1]=null;
}
returnresult;
}
}
如果tag的名字不是TAG_1995(名字是个梗),就调用函数createViewFromTag()创建View,进去看看,
ViewcreateViewFromTag(Viewparent,Stringname,AttributeSetattrs){
if(name.equals("view")){
name=attrs.getAttributeValue(null,"class");
}
......
Viewview;
if(mFactory2!=null)view=mFactory2.onCreateView(parent,name,mContext,attrs);
elseif(mFactory!=null)view=mFactory.onCreateView(name,mContext,attrs);
elseview=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("Createdviewis:"+view);
returnview;
......
}
首先尝试用3个Fractory创建View,如果成功就直接返回了。注意,我们可以利用这个机制,创建自己的Factory来控制View的创建过程。
如果没有Factory或创建失败,那么走默认逻辑。
先判断name中是否有'.'字符,如果没有,则认为使用android自己的View,此时会在name的前面加上包名"android.view.";如果有这个'.',则认为使用的自定义View,这时无需添加任何前缀,认为name已经包含全包名了。
最终,使用这个全包名的name来创建实例,
privatestaticfinalHashMap<String,Constructor<?extendsView>>sConstructorMap=
newHashMap<String,Constructor<?extendsView>>();
protectedViewonCreateView(Stringname,AttributeSetattrs)
throwsClassNotFoundException{
returncreateView(name,"android.view.",attrs);
}
publicfinalViewcreateView(Stringname,Stringprefix,AttributeSetattrs)
throwsClassNotFoundException,InflateException{
Constructor<?extendsView>constructor=sConstructorMap.get(name);
Class<?extendsView>clazz=null;
......
if(constructor==null){
//Classnotfoundinthecache,seeifit'sreal,andtrytoaddit
clazz=mContext.getClassLoader().loadClass(
prefix!=null?(prefix+name):name).asSubclass(View.class);
if(mFilter!=null&&clazz!=null){
booleanallowed=mFilter.onLoadClass(clazz);
if(!allowed){
failNotAllowed(name,prefix,attrs);
}
}
constructor=clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name,constructor);
}else{
//Ifwehaveafilter,applyittocachedconstructor
if(mFilter!=null){
//Haveweseenthisnamebefore?
BooleanallowedState=mFilterMap.get(name);
if(allowedState==null){
//Newclass--rememberwhetheritisallowed
clazz=mContext.getClassLoader().loadClass(
prefix!=null?(prefix+name):name).asSubclass(View.class);
booleanallowed=clazz!=null&&mFilter.onLoadClass(clazz);
mFilterMap.put(name,allowed);
if(!allowed){
failNotAllowed(name,prefix,attrs);
}
}elseif(allowedState.equals(Boolean.FALSE)){
failNotAllowed(name,prefix,attrs);
}
}
}
Object[]args=mConstructorArgs;
args[1]=attrs;
returnconstructor.newInstance(args);
......
}
从源码中看到,在创建实例前,会先从一个静态Map中获取缓存,
Constructor<?extendsView>constructor=sConstructorMap.get(name);
缓存的是Constructor对象,目的是用于创建实例,这里要注意sConstructorMap是静态的,并且通过Constructor创建的实例,是使用和Constructor对象同一个ClassLoader来创建的,换句话说,在同一个进程中,同一个自定义View对象,是无法用不同ClassLoader加载的,如果想解决这个问题,就不要让系统使用createView()接口创建View,做法就是自定义Factory或Factory2来自行创建View。
继续往下看,如果缓存里没有,则创建View的Class对象clazz,并缓存到sConstructorMap中,
if(constructor==null){
//Classnotfoundinthecache,seeifit'sreal,andtrytoaddit
clazz=mContext.getClassLoader().loadClass(
prefix!=null?(prefix+name):name).asSubclass(View.class);
if(mFilter!=null&&clazz!=null){
booleanallowed=mFilter.onLoadClass(clazz);
if(!allowed){
failNotAllowed(name,prefix,attrs);
}
}
constructor=clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name,constructor);
}
然后就是newInstance了,至此这个View便从xml中变成了java对象,我们继续返回到inflate函数中,看看这个View返回之后做了什么,
......
//Tempistherootviewthatwasfoundinthexml
Viewtemp;
if(TAG_1995.equals(name)){
temp=newBlinkLayout(mContext,attrs);
}else{
temp=createViewFromTag(root,name,attrs);
}
ViewGroup.LayoutParamsparams=null;
if(root!=null){
//Createlayoutparamsthatmatchroot,ifsupplied
params=root.generateLayoutParams(attrs);
if(!attachToRoot){
//Setthelayoutparamsfortempifwearenot
//attaching.(Ifweare,weuseaddView,below)
temp.setLayoutParams(params);
}
}
//Inflateallchildrenundertemp
rInflate(parser,temp,attrs,true);
//Wearesupposedtoattachalltheviewswefound(inttemp)
//toroot.Dothatnow.
if(root!=null&&attachToRoot){
root.addView(temp,params);
}
//Decidewhethertoreturntherootthatwaspassedinorthe
//topviewfoundinxml.
if(root==null||!attachToRoot){
result=temp;
}
......
returnresult;
从createViewFromTag返回后,会调用个rInflate(),其中parent参数就是刚才创建出的View,应该能猜到里面做了什么,
voidrInflate(XmlPullParserparser,Viewparent,finalAttributeSetattrs,
booleanfinishInflate)throwsXmlPullParserException,IOException{
finalintdepth=parser.getDepth();
inttype;
while(((type=parser.next())!=XmlPullParser.END_TAG||
parser.getDepth()>depth)&&type!=XmlPullParser.END_DOCUMENT){
if(type!=XmlPullParser.START_TAG){
continue;
}
finalStringname=parser.getName();
if(TAG_REQUEST_FOCUS.equals(name)){
parseRequestFocus(parser,parent);
}elseif(TAG_INCLUDE.equals(name)){
if(parser.getDepth()==0){
thrownewInflateException("<include/>cannotbetherootelement");
}
parseInclude(parser,parent,attrs);
}elseif(TAG_MERGE.equals(name)){
thrownewInflateException("<merge/>mustbetherootelement");
}elseif(TAG_1995.equals(name)){
finalViewview=newBlinkLayout(mContext,attrs);
finalViewGroupviewGroup=(ViewGroup)parent;
finalViewGroup.LayoutParamsparams=viewGroup.generateLayoutParams(attrs);
rInflate(parser,view,attrs,true);
viewGroup.addView(view,params);
}else{
finalViewview=createViewFromTag(parent,name,attrs);
finalViewGroupviewGroup=(ViewGroup)parent;
finalViewGroup.LayoutParamsparams=viewGroup.generateLayoutParams(attrs);
rInflate(parser,view,attrs,true);
viewGroup.addView(view,params);
}
}
if(finishInflate)parent.onFinishInflate();
}
没错,就是递归的使用createViewFromTag()创建子View,并通过ViewGroup.addView添加到parentview中。
之后,这个View树上的所有View都创建完毕。然后会根据inflate()的参数(root和attachToRoot)判断是否将新创建的View添加到rootview中,
if(root!=null&&attachToRoot){
root.addView(temp,params);
}
然后,inflate()就将View返回了。
以上内容是小编给大家介绍的android从xml加载到view对象过程解析,希望对大家有所帮助!