从XML变成View,它经历了啥?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从XML变成View,它经历了啥?相关的知识,希望对你有一定的参考价值。
参考技术A码个蛋(codeegg)第 626 次推文
作者:看我眼前007
原文:https://www.jianshu.com/p/eccd8ba87e8b
友情提示:
本文篇幅较长,建议在电脑上慢慢阅读~
写作背景
android 开发框架中,使用 Xml 文件描述 Ui 页面,通过setContentView(resId)或者LayoutInflater.inflate(resId,……)的方式把 Xml 文件描述的页面转换成 Java 对象。Xml 文件加上 AndroidStudio 提供的预览功能,使得 Android 开发过程中页面和业务逻辑可以并行开发,极大地提高了开发效率。
但是大部分 Android 工程师对 xml 文件如何转换成 Java 不是十分了解,本文将带大家一起探究 View 从 xml 文件到 Java 对象的转换过程
我们先罗列一下 xml 转换成 Java 对象的方式
我们一般在项目使用的 Activity 可能是
所有的 Activity 都是 android.app.Activity 的子类。
但是!每个继承 android.app.Activity 的子类 setContentView(resId) 实现方式都被重载了。我们这里先看最基础的 android.app.Activity
查看一下 getWindow源码
全局搜索 mWindow 对象赋值的地方找到以下代码
这里 PhoneWindow 的源码在 sdk 里面是隐藏的,我们去 androidxref ->PhoneWindow.java 查看 PhoneWindow.setContentView(layoutResID)
当我们没有设置转场动画的时候会执行
在 PhoneWindow 的构造函数中我们找到了 mLayoutInflater 对象赋值语句
所以我们得出一个结论
Activity.setContentView(resId) 最终还是使用
LayoutInflater.from(context).inflate(resId, ……)
再回头看下
android.support.v7.app. AppCompatActivity 和
android.support.v4.app. FragmentActivity
我们发现
android.support.v4.app. FragmentActivity 没有重载
android.app.Activity. setContentView (resId),
但是 android.support.v7.app. AppCompatActivity 重载了
再跟踪一下源代码我们发现最终会调用到
android.support.v7.app. AppCompatDelegateImplV9 . setContentView (resId)
这里我们又发现了 LayoutInflater的身影。
这里我们可以总结一下:
xml 转成成 Java对象是通过 LayoutInflater的inflate 方法来完成的。
LayoutInflater对象实例化
看一下 LayoutInflater的源码第一行
LayoutInflater 是一个抽象类, 抽象类是不能实例化的
看 Activity 的 getLayoutInflater
这里我们就可以看出 Activity 通过 getLayoutInflater 获取的是 PhoneWindow 的 mLayoutInflater (如果忘记了可以往上翻一下,或者去参考资料的链接里找找源码)
再看一下 LayoutInflater.from(context)
此时,我们必须请出柯南君帮我们宣布
真相只有一个!最终都是通过服务获取 LayoutInflater实例对象。
所以我们直接查看:
ContextImpl.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
继续跟踪 SystemServiceRegistry
这时候我们在 SystemServiceRegistry 类停留一下,发现这里似乎只注册各种系统服务的地方。
我们找到了
Context . LAYOUT_INFLATER_SERVICE 注册代码。
然后我们终于找到 LayoutInflater的实现类是 PhoneLayoutInflater
此时我们可以休息一下,喝口水,上个卫生间,进入下个阶段~
读取xml文件并创建View对象
再去源码查看一下,发现两个方法其实只有一个方法是核心,另一个只是做了一下封装,让我们少传入一个参数。
所以我们重点看一下 inflate(@LayoutRes int resource, @able ViewGroup root, boolean attachToRoot) 的源码
我们看到首先通过 res 对象把 resId 指向的 xml 文件转换为 XmlResourceParser 然后执行 inflate(parser, root, attachToRoot) 方法,该方法比较长,这里只贴出核心步骤。
以上步骤还是很长,我们将拆分几部分分析。
如果 xml 根标签是 merge,则 root 不能为空, attachToRoot 必须是 true。
然后执行 rInflate(parser, root, inflaterContext, attrs, false)
上面这个方式我们需要重点记一下
如果子节点是 include,则执行 parseInclude。
parseInclude的源码和 inflate(parser, root, attachToRoot)类似,都是读取xml对应的文件,转换成 XmlResourceParser 然后遍历里的标签。
经过层层调用,我们可以找到最终创建 View 的代码
第一部分代码,我们的到的结论是,
createViewFromTag(parent, name, context, attrs)负责创建 View 对象。
因为这里排除了merge标签,这里的根标签肯定是一个 View,所以调用了 createViewFromTag(root, name, inflaterContext, attrs)方法创建 View 。
再次印证了第一部分得出的结论 createViewFromTag (parent, name, context, attrs)负责创建 View 对象。
然后看下后面的代码我们就明白
inflate(@LayoutRes int resource, @able ViewGroup root, boolean attachToRoot) 三个参数的关系了
创建 View 对象
通过上面的判断我们终于找到了最最核心的方法 createViewFromTag
有包裹了一层,并且把 ignoreThemeAttr 设置为 false,表示这里会收到 Theme 的影响。
我们在 createViewFromTag(parent, name, context, attrs, false) 中找到了创建 View 的代码
这里又出现了 mFactory2、mFactory、mPrivateFactory 三个对象,似乎都是可以创建 View 。 对于android.app.Activity来说,这三个对象为 或者空实现(下一节会讲这个) 所以我们直接看
这里需要说明一下,如果 name属性里面含有 . 表示这是一个自定义 View,系统自带 View 我们可以省略类的路径,而自定义 View 则不能省略。
对于自定义 View 的创建,这里省略了大部分代码
仅仅看到 constructor . newInstanc e(args) ,我们已经明白这里使用了 反射创建 View 对象。
而对于 Android 内置的各种 View, 我们在 LayoutInflater 的实现类 PhoneLayoutInflater 中找到了重载
再看下 LayoutInflater 中的代码
我们可以看到, 对于系统内置的 View,会依次在 View 的标签前面加上
"android.widget."、"android.webkit."、"android.app." 、"android.view."
然后通过 反射 的方法 创建 View 。
(文章略有删减,可点击原文查看~)
到此 ,Xml 到 View 对象的转换过程全部结束~~~
看到这里的童鞋很辛苦!这项技能get了吗?
近期文章:
今日问题:
谁能来画个过程图?
画出来有信心的丢到码仔的学习群来,码仔给你发红包!
快来码仔社群解锁新姿势吧!社群升级:Max你的学习效率
View.post @Runnable 到底发生了啥 [重复]
【中文标题】 View.post @Runnable 到底发生了啥 [重复]【英文标题】:Does anyone know what really happen with View.post @Runnable [duplicate]有谁知道 View.post @Runnable 到底发生了什么 [重复] 【发布时间】:2021-11-15 11:54:35 【问题描述】:一个带有视图绑定的android项目,我注意到有viewBinding.root.post @Runnable
。
但我不知道这个 runnable 将如何影响我的根视图。 你能帮我吗:'(
【问题讨论】:
【参考方案1】:由于视图类中提供了 post 功能,您可以使用它在同一个视图上执行任何任务。例如重绘、无效、可见性等,视图绑定将为您提供根视图的视图对象。
这是资源文档中的一些信息
使 Runnable 添加到消息队列中。 runnable 将在用户界面线程上运行。
参数: action – 将被执行的 Runnable。
返回: 如果 Runnable 已成功放入消息队列,则返回 true。失败返回false,通常是因为处理消息队列的looper正在退出。
【讨论】:
以上是关于从XML变成View,它经历了啥?的主要内容,如果未能解决你的问题,请参考以下文章