我们在Activity开发的时候天天会用到这个方法,有时候还需要根据需求在setContentView调用的时候做一些动作,因此我们就需要知道它内部是如何工作的,我们来一起看一下:

setContentView有三个重载方法:

    public void setContentView(int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();}public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}public void setContentView(View view, ViewGroup.LayoutParams params) {getWindow().setContentView(view, params);initWindowDecorActionBar();}

他们实际都在调用getWindow方法返回的Window对象的setContentView方法,那getWindow对象返回的是谁呢?

    public Window getWindow() {return mWindow;}

我们可以看到getWindow返回的是一个mWindows的对象,那么它在哪被赋值的呢?通过查找我们可以找到在Activity的attach方法中有这么一段:

mWindow = PolicyManager.makeNewWindow(this);

也就是说继续往下看,发现PolicyManager.makeNewWindow内部确实这样的:

    // The static methods to spawn new policy-specific objectspublic static Window makeNewWindow(Context context) {return sPolicy.makeNewWindow(context);}

好,那我们找找sPolicy又是谁:

我们可以在PolicyManager方法内看到静态代码块以及它的一些属性:

    private static final String POLICY_IMPL_CLASS_NAME ="com.android.internal.policy.impl.Policy";private static final IPolicy sPolicy;static {// Pull in the actual implementation of the policy at run-timetry {Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);sPolicy = (IPolicy)policyClass.newInstance();} catch (ClassNotFoundException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);} catch (InstantiationException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);} catch (IllegalAccessException ex) {throw new RuntimeException(POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);}}

也就是说,sPolicy对象是类com.android.internal.policy.impl.Policy的一个实例,OK,我们进入这个类看一下它的makeNewWindow方法,噢噢,是这样啊:

    public Window makeNewWindow(Context context) {return new PhoneWindow(context);}

原来那个getWindow返回的是PhoneWindow对象,setContentView调用的则是PhoneWindow的setContentView,OK,说了这么多重点就是看下PhoneWindow的setContentView方法是如何工作的:

PhoneWindow的setContentView的重载方法内部有些不同,我们先看参数为int的setContentView:

    public void setContentView(int layoutResID) {// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window// decor, when theme attributes and the like are crystalized. Do not check the feature// before this happens.if (mContentParent == null) {installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {mContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());transitionTo(newScene);} else {mLayoutInflater.inflate(layoutResID, mContentParent);}final Callback cb = getCallback();if (cb != null && !isDestroyed()) {cb.onContentChanged();}}

我们只看重点部分,都是通过LayoutInflater的inflate的方法加载的,是不是很熟悉呢?inflate方法的第二个参数是mContentParent,就是说要把这个layoutResID的这个布局添加到mContentParent中,那么mContentParent就是我们的主布局了,从代码中也可以看出来:

    /*** The ID that the main layout in the XML layout file should have.*/public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
    protected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme....ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent == null) {throw new RuntimeException("Window couldn't find content container view");}...return contentParent;}
            mContentParent = generateLayout(mDecor);

好,入口的东西都分析的差不多了,着重看一下LayoutInflater的inflate的工作原理,我们常用的方法是这3个:

    public View inflate(int resource, ViewGroup root) {return inflate(resource, root, root != null);}public View inflate(XmlPullParser parser, ViewGroup root) {return inflate(parser, root, root != null);}public View inflate(int resource, ViewGroup root, boolean attachToRoot) {final Resources res = getContext().getResources();if (DEBUG) {Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("+ Integer.toHexString(resource) + ")");}final XmlResourceParser parser = res.getLayout(resource);try {return inflate(parser, root, attachToRoot);} finally {parser.close();}}

其实它们都没做什么,主要做工作的是它:

    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {synchronized (mConstructorArgs) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");final AttributeSet attrs = Xml.asAttributeSet(parser);Context lastContext = (Context)mConstructorArgs[0];mConstructorArgs[0] = mContext;View result = root;try {// Look for the root node.int type;while ((type = parser.next()) != XmlPullParser.START_TAG &&type != XmlPullParser.END_DOCUMENT) {// Empty}if (type != XmlPullParser.START_TAG) {throw new InflateException(parser.getPositionDescription()+ ": No start tag found!");}final String name = parser.getName();if (DEBUG) {System.out.println("**************************");System.out.println("Creating root view: "+ name);System.out.println("**************************");}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, false);} else {// Temp is the root view that was found in the xmlfinal View temp = createViewFromTag(root, name, attrs, false);ViewGroup.LayoutParams params = null;if (root != null) {if (DEBUG) {System.out.println("Creating params from root: " +root);}// Create layout params that match root, if suppliedparams = 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);}}if (DEBUG) {System.out.println("-----> start inflating children");}// Inflate all children under temprInflate(parser, temp, attrs, true, true);if (DEBUG) {System.out.println("-----> done inflating children");}// 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) {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;}Trace.traceEnd(Trace.TRACE_TAG_VIEW);return result;}}

这段代码比较多,我们挑一下重点看:

                    // Temp is the root view that was found in the xmlfinal View temp = createViewFromTag(root, name, attrs, false);ViewGroup.LayoutParams params = null;if (root != null) {// Create layout params that match root, if suppliedparams = 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);}}// 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;}

这段代码就是说通过createViewFromTag方法生成临时View对象,然后如果View生成成功,给它产生一个默认的LayoutParams对象附在上面,最后判断root是否为空,决定是否要将这个临时View添加到root之上,好,接下来,我们的重点是createViewFromTag方法:

    /*** Creates a view from a tag name using the supplied attribute set.* <p>* If {@code inheritContext} is true and the parent is non-null, the view* will be inflated in parent view's context. If the view specifies a* <theme> attribute, the inflation context will be wrapped with the* specified theme.* <p>* Note: Default visibility so the BridgeInflater can override it.*/View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) {if (name.equals("view")) {name = attrs.getAttributeValue(null, "class");}Context viewContext;if (parent != null && inheritContext) {viewContext = parent.getContext();} else {viewContext = mContext;}// Apply a theme wrapper, if requested.final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME);final int themeResId = ta.getResourceId(0, 0);if (themeResId != 0) {viewContext = new ContextThemeWrapper(viewContext, themeResId);}ta.recycle();if (name.equals(TAG_1995)) {// Let's party like it's 1995!return new BlinkLayout(viewContext, attrs);}if (DEBUG) System.out.println("******** Creating view: " + name);try {View view;if (mFactory2 != null) {view = mFactory2.onCreateView(parent, name, viewContext, attrs);} else if (mFactory != null) {view = mFactory.onCreateView(name, viewContext, attrs);} else {view = null;}if (view == null && mPrivateFactory != null) {view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs);}if (view == null) {final Object lastContext = mConstructorArgs[0];mConstructorArgs[0] = viewContext;try {if (-1 == name.indexOf('.')) {view = onCreateView(parent, name, attrs);} else {view = createView(name, null, attrs);}} finally {mConstructorArgs[0] = lastContext;}}if (DEBUG) System.out.println("Created view is: " + view);return view;} catch (InflateException e) {throw e;} catch (ClassNotFoundException e) {InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + name);ie.initCause(e);throw ie;} catch (Exception e) {InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + name);ie.initCause(e);throw ie;}}

这么多代码,其实我们的重点是这部分:

            View view;if (mFactory2 != null) {view = mFactory2.onCreateView(parent, name, viewContext, attrs);} else if (mFactory != null) {view = mFactory.onCreateView(name, viewContext, attrs);} else {view = null;}if (view == null && mPrivateFactory != null) {view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs);}if (view == null) {final Object lastContext = mConstructorArgs[0];mConstructorArgs[0] = viewContext;try {if (-1 == name.indexOf('.')) {view = onCreateView(parent, name, attrs);} else {view = createView(name, null, attrs);}} finally {mConstructorArgs[0] = lastContext;}}

我们可以看到,刚开始会交给mFactory2、mFactory、mPrivateFactory这三个View工厂来进行处理,这三个对象都是通过构造方法设置进来的,如下:

    protected LayoutInflater(LayoutInflater original, Context newContext) {mContext = newContext;mFactory = original.mFactory;mFactory2 = original.mFactory2;mPrivateFactory = original.mPrivateFactory;setFilter(original.mFilter);}

这个构造方法在哪里被调用我们先不去看,我们可以继续我们的重点:

                    if (-1 == name.indexOf('.')) {view = onCreateView(parent, name, attrs);} else {view = createView(name, null, attrs);}

这里的意思是,如果这个Tag是没有( . )的话,那就是说这个tag是android提供的默认的控件,像View,TextView,Button等等。否则,就是其它情况了,我们先看默认没有( . )的情况:

    protected View onCreateView(String name, AttributeSet attrs)throws ClassNotFoundException {return createView(name, "android.view.", attrs);}

请注意这个方法的第二个参数android.view.,然后我们进入方法内部:

    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;try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);if (constructor == null) {// Class not found in the cache, see if it's real, and try to add itclazz = 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 constructorif (mFilter != null) {// Have we seen this name before?Boolean allowedState = mFilterMap.get(name);if (allowedState == null) {// New class -- remember whether it is allowedclazz = 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;constructor.setAccessible(true);final View view = constructor.newInstance(args);if (view instanceof ViewStub) {// Use the same context when inflating ViewStub later.final ViewStub viewStub = (ViewStub) view;viewStub.setLayoutInflater(cloneInContext((Context) args[0]));}return view;} catch (NoSuchMethodException e) {InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class "+ (prefix != null ? (prefix + name) : name));ie.initCause(e);throw ie;} catch (ClassCastException e) {// If loaded class is not a View subclassInflateException ie = new InflateException(attrs.getPositionDescription()+ ": Class is not a View "+ (prefix != null ? (prefix + name) : name));ie.initCause(e);throw ie;} catch (ClassNotFoundException e) {// If loadClass fails, we should propagate the exception.throw e;} catch (Exception e) {InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class "+ (clazz == null ? "<unknown>" : clazz.getName()));ie.initCause(e);throw ie;} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}

这段代码其实重点就是通过ClassLoader的loadClass方法将类加载进来,然后通过反射的方式获取它的构造方法进行实例化,然后基本功能就完成了。有的同学可能会注意到这里有个mFilter属性,这个属性是用来定义这个类是否允许被加载的。

好,以上就是最基本的Inflate加载过程,其实实际过程不是这样的,我们将在下一章查看详细的加载过程。

从源码的角度说说Activity的setContentView的原理相关推荐

  1. 从源码的角度说说Activity的setContentView的原理(二)

    前文http://blog.csdn.net/sahadev_/article/details/49072045虽然讲解了LayoutInflate的整个过程,但是其中很多地方是不准确不充分的,这一节 ...

  2. 从 Android 6.0 源码的角度剖析 Binder 工作原理 | CSDN 博文精选

    在从Android 6.0源码的角度剖析Activity的启动过程一文(https://blog.csdn.net/AndrExpert/article/details/81488503)中,我们了解 ...

  3. 从Android 6.0源码的角度剖析View的绘制原理

    在从Android 6.0源码的角度剖析Activity的启动过程和从Android 6.0源码的角度剖析Window内部机制原理的文章中,我们分别详细地阐述了一个界面(Activity)从启动到显示 ...

  4. Android Fragment 从源码的角度去解析(上)

    ###1.概述 本来想着昨天星期五可以早点休息,今天可以早点起来跑步,可没想到事情那么的多,晚上有人问我主页怎么做到点击才去加载Fragment数据,而不是一进入主页就去加载所有的数据,在这里自己就对 ...

  5. Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    <div id="container">         <div id="header">     <div class=&qu ...

  6. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread -> Activity、主线程阶段 二 )

    文章目录 前言 一.ActivityThread 类 handleLaunchActivity -> performLaunchActivity 方法 二.Instrumentation.new ...

  7. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

  8. 【动态代理】从源码实现角度剖析JDK动态代理

    相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象.动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代 ...

  9. [学习总结]7、Android AsyncTask完全解析,带你从源码的角度彻底理解

    我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴 ...

最新文章

  1. 【Vue】24.遮罩层阻止默认滚动事件
  2. 系统解读:权限设计指南
  3. php商品分类显示商品,ecshop首页显示全部商品分类的方法
  4. android系统五大布局,android 五大布局文件
  5. php电影推荐算法,每周一道算法题013:电影推荐
  6. Kotlin 或将取代 Java —— 《Java 编程思想》作者 Bruce Eckel
  7. html5如何将4张照片排列,如何将多张图片排列在一张图片呢?学会这两种技巧,轻松搞定...
  8. visio画图复制粘贴到word_用VISIO画图 复制完之后粘贴到word中为什么只显示下面一部分?...
  9. IP报文头详解以及定义
  10. css实现超过两行用...表示
  11. 企企通携手“浙江制造”品牌【安诺化学】,一站式采购管理助推企业数字化建设
  12. 读论文:Self-Attention ConvLSTM for Spatiotemporal Prediction
  13. 生成式对抗网络(GAN)实战——书法字体生成练习赛
  14. 微服务架构师封神之路09-Springboot多数据源,Hikari连接池和事务配置
  15. 【QTdesigner】课时36.使用QTextEdit控件输入多行文本【pyqt5+QTdesigner模式】
  16. 6.redis-哨兵
  17. XYOJ1255: 寻找最大数X(按数的一个一个元素输出)
  18. 双子星IPTV桌面APK源码 网络电视机顶盒直播APP源码 带php后台
  19. 全志H6开发板香橙派OrangePi 3 LTS在 Linux系统中安装Home Assistant的方法(上篇)
  20. html表白画画,另类“表白”的手帐,简单有爱的简笔画,也能给你满满的甜蜜感!...

热门文章

  1. Linux kernel 中模块化的平台驱动代码介绍
  2. Kernel中如何操作CPU及外设寄存器
  3. linux inputuevent使用
  4. Python3——函数
  5. Datawha组队——Pandas(下)综合练习(打卡)
  6. sklearn——决策树
  7. idea spring tomcat启动失败_技术篇 | 实用IDEA插件和工具系列
  8. 【Pytorch神经网络基础理论篇】 07 线性回归 + 基础优化算法
  9. Uniapp学习笔记(数据展示、数据循环、条件编译、计算属性、组件的使用、组件插槽、生命周期)
  10. Scala 入门2(数组、List、Set、Map、元组、Option、Iterator)