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