0


Android setContentView流程分析(二)

  上一边文章中我们讲了setContentView的过程是如何拿到DecorView和contentParent的,这节我们将讲解拿到contentParent后,如何将R.layout.activity_main.xml的View创建出来
  无论MainActivity是继承Activity还是继承AppCompatActivity它们创建View部分代码都是LayoutInflater的inflate方法创建View的

我们就从这里开始

LayoutInflater.from(mContext).inflate(resId, contentParent);

inflate通过多次调用就会进入下述inflate中

publicViewinflate(XmlPullParser parser,@NullableViewGroup root,boolean attachToRoot){synchronized(mConstructorArgs){...//省略代码try{....//省略代码//如果是merge标签的就必须有父布局attachToRoot必须为true,不然这里就会抛出异常,这个于merge标签的属性有关if(TAG_MERGE.equals(name)){if(root ==null||!attachToRoot){thrownewInflateException("<merge /> can be used only with a valid "+"ViewGroup root and attachToRoot=true");}rInflate(parser, root, inflaterContext, attrs,false);}else{// Temp is the root view that was found in the xml//这创建rootView,这个rootView就是R.layout.activity_main.xml的根ViewfinalView temp =createViewFromTag(root, name, inflaterContext, attrs);...//创建子的View// Inflate all children under temp against its context.rInflateChildren(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;}}}catch(XmlPullParserException e){...//省略代码}catch(Exception e){...//省略代码}finally{...//省略代码}return result;}}

1.创建R.layout.activity_main.xml的根View

finalView temp =createViewFromTag(root, name, inflaterContext, attrs);

createViewFromTag()方法经过层层调用最终调用到下边代码

@UnsupportedAppUsageViewcreateViewFromTag(View parent,String name,Context context,AttributeSet attrs,boolean ignoreThemeAttr){...//省略代码try{View view =tryCreateView(parent, name, context, attrs);if(view ==null){..//省略代码try{if(-1== name.indexOf('.')){//如果sdk中的view空间将会走这个逻辑
                        view =onCreateView(context, parent, name, attrs);}else{//自定义view将会走这里的逻辑
                        view =createView(context, name,null, attrs);}}finally{
                    mConstructorArgs[0]= lastContext;}}return view;}catch(InflateException e){...//省略代码}catch(ClassNotFoundException e){...//省略代码}catch(Exception e){...//省略代码}}

先来看onCreateView(),经过层层调用会LayoutInflater.java中的

protectedViewonCreateView(View parent,String name,AttributeSet attrs)throwsClassNotFoundException{returnonCreateView(name, attrs);}

这里需要注意的这两参的

onCreateView(name, attrs);

不是直调用LayoutInflater.java中的两参onCreateView方法,而是调用到PhoneLayoutInflater.java中的

onCreateView(String name, AttributeSet attrs)

,因为LayoutInflater是一个抽象类,它是由PhoneLayoutInflater类实现的

@OverrideprotectedViewonCreateView(String name,AttributeSet attrs)throwsClassNotFoundException{for(String prefix : sClassPrefixList){try{//调用到LayoutInflater中的三参方法View view =createView(name, prefix, attrs);if(view !=null){return view;}}catch(ClassNotFoundException e){// In this case we want to let the base class take a crack// at it.}}//调用到LayoutInflater中的两参方法returnsuper.onCreateView(name, attrs);}
privatestaticfinalString[] sClassPrefixList ={"android.widget.","android.webkit.","android.app."};

  这里的目的是通过sClassPrefixList 将空间的类名补全最后通过反射的方法创建View
  从PhoneLayoutInflater的onCreateView()方法中看这里可以循环sClassPrefixList 数据来遍历补全空间的名称,当在sClassPrefixList 中没找到对应的空间的时候,就会返回父类的两参

onCreateView(name, attrs);


这里又回到了LayoutInflater

protectedViewonCreateView(String name,AttributeSet attrs)throwsClassNotFoundException{returncreateView(name,"android.view.", attrs);}

  LayoutInflater的

onCreateView(String name, AttributeSet attrs)

方法也有一个"android.view."的前缀,相当于创建一个View的时候遍历了四次如果还没找到这个空间类,那么创建View就会出错

  经过一系列的遍历调用最后会调用到LayoutInflater的

createView(@NonNull Context viewContext, @NonNull String name, @Nullable String prefix, @Nullable AttributeSet attrs)

方法通过反射来创建View了

publicfinalViewcreateView(@NonNullContext viewContext,@NonNullString name,@NullableString prefix,@NullableAttributeSet attrs)throwsClassNotFoundException,InflateException{...//省略代码try{...//省略代码if(constructor ==null){// Class not found in the cache, see if it's real, and try to add it    //通过反射创建class的对象
                clazz =Class.forName(prefix !=null?(prefix + name): name,false,
                        mContext.getClassLoader()).asSubclass(View.class);...//省略代码//拿到构造对象
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);}else{...//省略代码}...//省略代码try{//创建ViewfinalView view = constructor.newInstance(args);if(view instanceofViewStub){// Use the same context when inflating ViewStub later.finalViewStub viewStub =(ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));}return view;}finally{...//省略代码}}catch(NoSuchMethodException e){...//省略代码}catch(ClassCastException e){...//省略代码}catch(ClassNotFoundException e){...//省略代码}catch(Exception e){...//省略代码}finally{...//省略代码}}

这里通过三步创建出View
(1) 通过反射创建class的对象

 clazz =Class.forName(prefix !=null?(prefix + name): name,false,
                        mContext.getClassLoader()).asSubclass(View.class);

(2)拿到构造对象

  constructor = clazz.getConstructor(mConstructorSignature);

(3)创建View

finalView view = constructor.newInstance(args);

21.创建R.layout.activity_main.xml的子View

rInflateChildren(parser, temp, attrs,true);
*/voidrInflate(XmlPullParser parser,View parent,Context context,AttributeSet attrs,boolean finishInflate)throwsXmlPullParserException,IOException{...//省略代码//这里会遍历整个R.layout.activity_main.xml创建其中的Viewwhile(((type = parser.next())!=XmlPullParser.END_TAG ||
                parser.getDepth()> depth)&& type !=XmlPullParser.END_DOCUMENT){...//省略代码if(TAG_REQUEST_FOCUS.equals(name)){...//省略代码}elseif(TAG_TAG.equals(name)){...//省略代码}elseif(TAG_INCLUDE.equals(name)){...//省略代码}elseif(TAG_MERGE.equals(name)){thrownewInflateException("<merge /> must be the root element");}else{//这里会递归的创建各个层级的View,createViewFromTag的逻辑和前边的都一样finalView view =createViewFromTag(parent, name, context, attrs);finalViewGroup viewGroup =(ViewGroup) parent;finalViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);rInflateChildren(parser, view, attrs,true);
                viewGroup.addView(view, params);}}...//省略代码}

这里就会创建出R.layout.activity_main.xml中的所有View。
至此,整个R.layout.activity_main.xml布局中的所有View都被通过反射的方法创建出来,setContentView的工作结束,这里只是在DecorView中创建了View,DecorView还未被添加到Window中。


本文转载自: https://blog.csdn.net/Gary1_Liu/article/details/129508574
版权归原作者 Gary1_Liu 所有, 如有侵权,请联系我们删除。

“Android setContentView流程分析(二)”的评论:

还没有评论