Android之View的诞生之谜

来源:互联网 发布:伦敦公共交通网络 编辑:程序博客网 时间:2024/05/01 13:50

作者博客

http://www.cherylgood.cn

前言

hello,大家好,平时大家都说自定义view,这次给大家带来有关view的相关知识,希望你喜欢!

作为一名正在岗位上的Android开发者,工作中常常需要我们使用自定义View去实现一些天马行空的效果,而作为一名正在寻找工作的Android开发者而言,面试过程中自定义View的相关知识点也是热门的面试题目之一哦,好东西我们怎么能错过呢;

作为Android开发者,我们应该不断的丰富自身的知识体系结构,加强Android开发内功的修炼(个人看法:学习Android内部底层一些的知识,可视为内功。而对于api的灵活使用,可视为招式)。

本次我们将来探索自定义View的内功心法之自定义View的死亡三部曲:测量、布局、绘制。

在了解死亡三部曲之前,我们先从上层的视角看下死亡三部曲的执行流程。

Activity的布局文件是如何被加载的?

我们的activity中的视图是什么时候被加载的呢?setContentView(R.layout.main);这个方法你肯定会很眼熟:其实我们的activity就是通过这个方法加载我们的布局文件进行视图的渲染。那么我们就从他入手吧。

我们进入setContentView(R.layout.main)的源码看一下,注意代码中的注视:

window是什么东东?window是一个抽象类,他只有一个实现类,那就是phoneWindow,phoneWindow是android系统中窗口的顶级类。

我们接着看 getWindow().setContentView(layoutResID);

在渲染我们的布局文件前,先调用了installDecor()来初始化mContentParent,之前也说mContentParent是负责加载我们页面内容的容器,到底是不是呢?我们看下installDecor源码便知道了:

从2处我们看到mContentParent被创建,那么它是如何被创建的呢,他真的是如我们前面所说负责加载内容部分的父容器么?我们来一探究竟,我们看 mContentParent = generateLayout(mDecor)的源码:

小小的发现:从上面的代码我们可以解释很多开发中的技巧,看下面的代码,在加载我们的资源文件前,他就检查了FEATURE_ACTION_BAR和FEATURE_NO_TITLE属性,所以我们想让activity全屏或者没有actionBar的话,必须在setContentView调用之前设置。

接下来我们回到前面

setContentViewgetWindow().setContentView(layoutResID);方法,继续看mLayoutInflater.inflate(layoutResID, mContentParent); 这个方法 mContenParent我们已经知道是什么了,然后通过mLayoutInflater.inflate,我们的布局就被渲染出来了。

DecorView补充: DecorView是整个ViewTree的最顶层View,我们之前分析过她是是个FrameLayout布局,代表了整个应用的界面。在该布局下面,有标题view和内容view这两个子元素,而内容view则是上面提到的mContentParent。如下图:

到目前为止,通过setContentView实例化了DecorView并且加载了设置进来的布局文件。然后,并没有发现任何与测量、布局、绘制相关的点,可能你会想,我们不会搞错了吧,其实没有哦,你们想想,setContentView实在,既然还是不可见的,那我为什么要耗费资源去测量呢,你最终能不能露个脸还说不准呢。亏本的买卖咱不干。其实要想知道什么时候开始执行测量等工作,我们可以看下ActivityThread的源码,ActivityThread是android用来管理activity的,这家伙知道的肯定多一些。那么我们就来了解下ActivityThread的执行流程。

首先ActivityThread通过调用handleLaunchActivity启动我们的目标activity

也就是说在performLaunchActivity调用之后,activity的onCreate被调用,我们的资源文件不加载,但是此时还是不可见的,也就还没有进行侧脸之类的事情。

然后我们继续看ActivityThread.handleResumeActivity的源码:

知识补充:

Window是一个抽象的概念,一个Window对应一个View和一个ViewRootImpl;

Window和View是通过ViewRootImpl联系起来的。

ViewRootImpl才是一个View真正实现的动作。

WindowManager中也有一个WindowManagerImpl作为实现的类,负责具体的操作。

跟到这里,我们来总结一下,activity启动过程中,在执行handleResumeActivity时将我们的顶层视图DecorView通过WindowManager挂载到window中。

而WindowManager是个接口类,那么我们看看其实类对象WindowManagerImpl.addView方法

mGlobal其实是WindowManagerGlobal的一个内部实例,接着看WindowManagerGlobal.addView的源码:

我们继续看ViewRootImpl.setView方法的源码

setView完成的工作很多,如声明输入事件的管道,DisplayManager的注册,view的绘画,window的添加等等

作为绘制view的入口,我们来看下requestLayout方法

ViewRootImpl.scheduleTraversals()调用后,系统会发起一个异步消息,然后在异步消息执行过程中调用performTraversals()完成具体的View树遍历;

小子,总算是找到你了,我们来看下胜利的果实吧!

总结

通过上面内容,我们学到了一些小技巧,如移除状态栏的一些步骤,之前我们可能知道,嗯,是的,要在setContentView前调用requestFeature才可以,通过这次分析,我们之前可能是知道要这样子做才行,现在我们知道了为什么要这样子做。是不是写起代码来更踏实了呢?

通过这次分析,我们对于activity的创建流程也略知一二,希望对你有帮助

测量、布局、绘制的工作我们放到下一章节进行学习

原创粉丝点击