一篇文章教你读懂UI绘制流程
最近有好多人问我Android没信心去深造了,找不到好的工作,其实我以一个他们进行回复,发现他们主要是内心比较浮躁,要知道技术行业永远缺少的是高手。建议先阅读浅谈Android发展趋势分析,在工作中,总结菜式王道,沉淀才能成长!
前言
在android当中对于UI体系当中往往我们会在绘制UI的时候碰到各种各样的问题而不知道从何解决, 也有时需要开发更改自定义组件时,需要做自己的调整,或者是实现某个自定义特效时的思路不明确,想要达到去玩转UI的最为基础的部分,就是去全面的深入了解UI的绘制流程.所以接下来带大家去进行全面分析UI整体的绘制体系.
思路:android程序启动--->Activity加载并完成生命周期--->setContentView--->图形绘制
疑惑:
1.Android程序是如何启动,Activity生命周期如何调用?
2.在Activity onCreate当中我们的setContentView是如何将UI文件加载?
3.UI是如何绘制的?
答案:
1.Android程序流程
众所周知,我们的java程序想要开启需要依赖于main方法,也就是我们的程序入口(主线程)进入,但是在我们日常开发android程序的过程当中我们并没有发现main方法的存在,那么android当中的是如何开始运行的?
熟悉的朋友们可能都知道在android当中存在一个叫做ActivityThread的类,这个类代表的是android当中的主线程,而在这个类当中我们看到了比较熟悉的main方法,那么现在是否可以认为我们的android在打开app时是首先调用的是当前这个类的main,也就是此处为我们的启动点
在此处可以看到Activity调用了一个attach()方法
在这里我们可能首先要考虑的是getService拿出来的是什么?
进去之后,我们会发现
在这个当中,里面调用了的系统的ActivityManagerService这个服务,并且给出了一个Binder接口
那么在这里,我们可以联想到,在android当中的binder通信机制,那么实际上我们的ActivityManager是有系统服务所调用管理,并且通过在binder接口当中进行调用,这也是为什么我们讲Activity是跨进程访问的原因
那么明白了这个时候能够得到ActivityManager之后,我们接着回到attach当中继续看下去, 这个时候会发现,我们调用了一个attachApplication方法(见图2)这个方法又是干嘛的?attachApplication在这里的作用其实实际上是ActivityThread通过attach获取到,然后将applciationThread将其关联,把activity相关信息存储在applciationThread里面,apllicationThread的类为activity的各种状态做了相对应的准备工作
这个时候我们需要关注,ApplicationThread当中做了什么?
当我们打开ApplicationThread中我们会看到一堆的schedle方法,这些方法的名称其实就可以给我们表明,代表的是在执行Activity的某种状态时调用的计划执行方法
这时我们会看到一个scheduleLaunchActivity方法,表示计划加载时调用的
这里我门发现了一个很有意思的事情
这个上面我们会看到一个ActivityClientRecord对象,这个对象其实实际上就是我们的Activity
而且似乎每一个方法还干了一件让我们非常熟悉的一件事, 进行了一次sendMessage()将当前创建的Activity发送了出去
当走到这里我们会发现最终我们调用的是Handler的消息通信机制,也就是说,在这里我们可以总结一下,
当Activity状态改变时,都会有对应的一个消息发送出去
而接收这里,我能发现通过发送时不同的状态,这边调用了不同的handlerXXXActivity方法
在这里,我门貌似发现了Activity的生命周期的调用痕迹,那么其实到此为止,我门可以得出一个结论,
Application运行的过程当中,对于Activity的操作,状态转变,其实实际上是通过Handler消息机制来完成的,
Application当中只管去发, 由消息机制负责调用,因为在main方法当中我门的Looper轮训器是一直在进行轮训的
而当我们在加载Activity的时候,当中调用了一个performLaunchActivity()方法,在这个中间我发现了我们onCreate的调用痕迹
也就是说,到目前为止我们能够明白,整个Application加载Activity的整套流程是怎么回事
那么接下来我们需要关注的是,在onCreate当中我们所写的setContentView到底干了什么
2.setContentView
在onCreate当中我们往往会使用setContentView去进行设置我们自己的布局文件或者view,那么在这当中他到底是怎么做的?通过观察源码,这个时候通过一系列线索我找到了最终的位置PhoneWindow类
这个时候我们会看到他做了两个事情,一个是installDecor,另一个是inflate,这两个后一个不难猜出他是在进行布局文件的解析, 前面的我们认为她是在初始化某个东西
进来之后发现他初始化了两个东西,一个叫做mDecor,一个叫做mContentParent
我们看到了mDecor是一个DecorView
mContentParent是一个ViewGroup
透过注释的翻译,其实我们就能很明确知道这两个是用来干嘛的
// This is the view in which the window contents are placed. It is either(这是放置窗口内容的视图)
// mDecor itself, or a child of mDecor where the contents go.(它要么是mDecor本身,要么是mDecor的子类的内容。)
//This is the top-level view of the window, containing the window decor.(这是在窗口当中的顶层View,包含窗口的decor)
一个代表的是顶层view,一个用来装他下面的视图内容
在接着往下看的时候,我门发现,generateLayout方法当中,发现了在此处进行了大量的requestFeature的调用,也就是所,我们的requestFeature
然后在下面我门会发现在做了一件事情,
当前这里竟然在加载布局文件,并且生成了一个view, 但是好像貌似不是我门自己的
所以我们需要去探寻他到底加载了一个什么东东?
这是我找到了一个比较有意思的组件,
在这个上面我看到了一句这样的注释
//This is an optimized layout for a screen, with the minimum set of features
enabled.
这是一个屏幕的优化布局,具有最小的特征集启用。
通过注释和一些资料分析, 得到了一个比较坑的结果。
这是DecorView默认的一个渲染,然后我门自己的布局都是渲染到她的FrameLayout上的
那么在这里我门现在能够明白,installDector其实实际上是在初始化两个视图容器,然后加载系统的R资源及特征,产生了一个基本布局
那么接着回到之前我门关注的另外一个方法mLayoutInflater.inflate(layoutResID, mContentParent);
这个方法就比较好理解了,
这这段注释上面我门就可以得到一个信息
//Inflate a new view hierarchy from the specified xml resource.(从指定的视图当中获取试图的层次结构,意思就是,现在在加载自己的资源)
而具体流程就不贴代码了给各位上一张图
那么在这里我门就能够明白,setContentView其实做了两件比较核心的事情,就是加载环境配置,和自己的布局,那么接下来我门需要考虑的事情就是,他到底怎么画到界面上的
3.UI是如何绘制的?
通过前面两个章节,我门了解到,程序对于activity生命周期的调用,以及我们的视图资源的由来。这是我门需要找到的是我门的绘制起点在哪?
在ActivityThread启动时, 我发现在加载handleLaunchActivity方法调用performLaunchActivity方法之后又调用了一个handleResumeActivity在这里我发现了绘制流程的开始
通过前面的流程我门知道,onCreate之行完成之后,所有资源交给WindowManager保管
在这里,将我们的VIew交给了WindowManager,此处调用了addView
进入addView之后我们发现了一段这样的代码,他将视图,和参数还有我门的一个ViewRoot对象都用了容器去装在了起来,那么在此处我门可以得出,是将所有的相关对象保存起来
mViews保存的是View对象,DecorView
mRoots保存和顶层View关联的ViewRootImpl对象
mParams保存的是创建顶层View的layout参数。
而WindowManagerGlobal类也负责和WMS通信
而在此时,有一句关键代码root.setView,这里是将我们的参数,和视图同时交给了ViewRoot,那么这个时候我们来看下ViewRoot当中的setView干了什么
终于在这里让我发现了让我明白的一步
在这里我门会看到view.assignParent的设置是this, 那么也就是说在view当中parent其实实际上是ViewRoot
那么在setContentView当中调用了一个setLayoutParams()是调用的ViewRoot的
而在ViewRoot当中发现了setLayoutParams和preformLayout对requestLayout方法的调用
在requestLayout当中发现了对scheduleTraversals方法的调用而scheduleTraversals当中调用了doTraversal的访问,最终访问到了performTraversals(),而在这个里面,我发现了整体的绘制流程的调用
当前里面依次是用了
UI绘制先回去测量布局,然后在进行布局的摆放,当所有的布局测量摆放完毕之后,进行绘制。
至此整体UI绘制过程我们就已经非常清楚了。
我门可以根据这种绘制的流程来操作自己的自定义组件。
作者:barry 链接:https://www.jianshu.com/p/0b1d1124c002,转载请注明原创
总结
对于我们APP开发者来讲,建议大家不要以市场行情的变化而受影响,坚持自己喜欢人认定的路线走下去,沉淀和进步!
如果有什么的问题,欢迎随时来撩我。
一篇文章教你读懂UI绘制流程的更多相关文章
- 一篇文章教你读懂Makefile
makefile很重要 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professiona ...
- 一篇文章让你读懂Pivotal的GemFire家族产品
一篇文章让你读懂Pivotal的GemFire家族产品 学习了:https://www.sohu.com/a/217157517_747818
- 一篇文章带您读懂List集合(源码分析)
今天要分享的Java集合是List,主要是针对它的常见实现类ArrayList进行讲解 内容目录 什么是List核心方法源码剖析1.文档注释2.构造方法3.add()3.remove()如何提升Arr ...
- 就这?一篇文章让你读懂 Spring 事务
什么是事务 ▲ 百度百科 概括来讲,事务是一个由有限操作集合组成的逻辑单元.事务操作包含两个目的,数据一致以及操作隔离.数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效:事务回滚 ...
- 一篇文章让你读懂 OpenStack 的起源、架构和应用
OpenStack 是一个面向 IaaS 层的开源项目,用于实现公有云和私有云的部署及管理.拥有众多大公司的行业背书和数以千计的社区成员, OpenStack 被看作是云计算的未来.目前 OS 基金会 ...
- 一篇文章让你读懂iOS和Android的历史起源
智能手机虽说是移动电话,但我们完全可以将其作为小型化的电脑来思考.这样一来也能够显示出智能手机OS的高性能.我们首先一起来回顾下智能手机OS的历史. OS的黎明期 其实在很早之前就已经有这样的想法,即 ...
- 一篇文章带您读懂Map集合(源码分析)
今天要分享的Java集合是Map,主要是针对它的常见实现类HashMap进行讲解(jdk1.8) 什么是Map核心方法源码剖析1.文档注释2.成员变量3.构造方法4.put()5.get() 什么是M ...
- 一篇文章教你搞懂日志采集利器 Filebeat
关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 本文使用的Filebeat是7.7.0的版本,文章将从如下几个方面说明: Filebeat是什 ...
- 读懂UI设计的心理学
好文转载,版权归原作者 作为UI设计师,对待用户就像对待婴儿,知道如何通过界面设计诱导用户非常重要,这就需要了解心理学方面的知识了.今天分享一篇日本设计师的好文,结合心理学与设计,教你读懂心理学,提高 ...
随机推荐
- Hyperic-Sigar简介——检测与监控
http://blog.csdn.net/liyong199012/article/details/20302761 Hyperic-Sigar是一个收集系统各项底层信息的工具集.他有如下特点: 1. ...
- 求f(n)=[n/1]+[n/2]+---+[n/n]的值 简单杂题
这种小题首先根据 n/1+n/2+n/3+--+n/n=nlogn+欧拉常数r 可以知道 1e12的范围也不会爆longlong,不需要写高精度(到现在都不会写) 再根据数据范围可知O(n)级别的暴力 ...
- Spring 快速开始 启动Spring
[启动Spring必须配置] [web.xml部署描述符方式] 1.配置Servlet级别上下文 <servlet> <servlet-name>springDispatche ...
- Kali Linux之web安全扫描器skipfish使用
0x00.skipfish简介 谷歌公司出品的开源web程序评估软件. skipfish特点:CPU资源占用低,扫描速度快,每秒可以轻松处理2000个请求,误报率低. 1x00.skipfish使用 ...
- luogu P4156 [WC2016]论战捆竹竿
传送门 官方题解(证明都在这) 神仙题鸭qwq 转化模型,发现这题本质就是一个集合,每次可以加上集合里的数,问可以拼出多少不同的数 首先暴力需要膜意义下的最短路,例题戳这 然后这个暴力可以优化成N^2 ...
- Spring中@Transactional(rollbackFor = Exception.class)的作用
Spring中的@Transactional(rollbackFor = Exception.class)事务处理,当你的方法中抛出异常时,它会将 事务回滚,数据库中的数据将不会改变,也就是回到进入此 ...
- Android自定义控件三种方式
1.组合原生控件(继承自ViewGroup.LinearLayout.FrameLayout.RelativeLayout等) 将原生空间做组合,自定义一些事件 2.自己绘制控件(继承自View) ...
- jquery判断表单内容是否为空
//判断表单数据是否为空 var t = $('form').serializeArray(); $.each(t,function(i,item){ if(item['value'] == '') ...
- 【Convex Optimization (by Boyd) 学习笔记】Chapter 2 - Convex sets(1) 仿射集&凸集
I. 仿射凸集(Affine and convex sets) 1. 线与线段 假设\(R^n\)空间内两点\(x_1,x_2\, (x_1≠x_2)\),那么\(y=\theta x_1+(1-\t ...
- mybatis-plus调用自身的 selectById 方法报错:org.apache.ibatis.binding.BindingException:
mybatis-plus的版本号是 2.0.1,在调用自身的insert(T)的时候没有报错,但是执行update报错,调用selectById.deleteById的时候也报错.也就是涉及到需要主键 ...