主要内容:ViewTreeObserver 是被用来注册监听视图树的观察者,在视图树发生全局改变时将收到通知。本文从 ViewTreeObserver 源码出发,带你剖析 ViewTreeObserver 的设计及使用,并间接体会观察者模式、Android消息传递机制在其中的使用。

这两天看代码看到了 ViewTreeObserver ,之前有接触过,但一直不太其到底在表达什么。这次既然又碰到了,那就干脆研究一下吧。于是我开始以关键字“ViewTreeObserver” Google 相关内容,结果发现找到的内容都差不多,更有甚者通篇直接翻译api,惊呆了!真的一点都不夸张,不相信的可以自己搜索试试看…

本系列分为上下两篇,本篇较为基础,主要介绍 ViewTreeObserver 的基础信息,以及说一下观察者模式,至于消息传递以及实战等更多内容将在下篇介绍。
1. 分开理解 ViewTreeObserver

ViewTreeObserver,拆开来理解就是 ViewTree 和 Observer。

ViewTree 就是我们常说的视图树,在Android中,所有视图都是由View及View的子类构成,ViewGroup 也是View的子类,它可以装载View或者ViewGroup,这样,一层层嵌套,就有了视图树的概念。给一张图感受一下:

视图树

Observer 即观察者的意思,其对应的观察者模式是软件设计模式的一种,在许多编程语言中被广泛运用。其核心在于:一个目标对象,我们称之为被观察者(Observable),管理着所有依附于它的观察者(Observers),当被观察者发生某种改变时(称为事件),被观察者调用对该事件感兴趣的观察者所提供的方法,主动通知观察者。

总结起来就是三要素:观察者,被观察者,事件,图示如下,图画的有点虚。

观察者和被观察者

至于在 ViewTreeObserver 中,观察者模式是如何工作的,需要留到后面再说了,因为涉及到 View 的 measure、layout 和 draw 等过程。等不及的可以自己看一下,给点提示:看 ViewRootImpl 类。
2. ViewTreeObserver 概念

分别介绍 ViewTree 和 Observer 之后,我们就不难理解 ViewTreeObserver 的概念了。在这里,ViewTree 即为被观察者(也可以是单个View),ViewTreeObserver 就是观察者,ViewTreeObserver 对 ViewTree 注册监听(观察它),当 ViewTree 发生某种变化时,将调用 ViewTreeObserver 的相关方法,通知其这一改变。我们要做的就是覆写这一方法,添加自己的逻辑。

来看一下官方文档的注释说明:

ViewTreeObserver 是被用来注册监听视图树的观察者,在视图树发生全局改变时将收到通知。这种全局事件包括但不限于:整个视图树的布局发生改变、在视图开始绘制之前、视图触摸模式改变时…
3. 获取 ViewTreeObserver 对象

了解了 ViewTreeObserver 之后,接下来说说如何获取 ViewTreeObserver 对象。我想大多数人会想到 new ViewTreeObserver(),因为这个我们最擅长了。

但是,ViewTreeObserver 的构造方法明确声明了:This constructor should not be called。也就是我们没法调用,当我们尝试这么做时,IDE 会提示:

'ViewTreeObserver()' is not public in 'android.view.ViewTreeObserver'.Cannot be accessed from outside package

根据 ViewTreeObserver 类的注释可知,我们只能通过 View 的 getViewTreeObserver() 方法获取 ViewTreeObserver 对象,那么我们看一下 getViewTreeObserver() 方法的源码:

getViewTreeObserver

先介绍一下方法中的 AttachInfo,官方文档如此注释说明:

A set of information given to a view when it is attached to its parent

意思是:当这个 View 被附着到父窗口时,将会获得这一组信息。说的好抽象啊,还是自己这组信息都有啥吧。

AttachInfo 是 View 类的内部类,找到这个类,浏览一遍,发现其含了一个 ViewTreeObserver 对象,以及该 View 所处的窗口(Window)、设备(Display)、根视图(rootView)等信息,信息量还是蛮大的,感兴趣的可以自己了解下,我们目前只关心这个 ViewTreeObserver 对象。

下面分析方法的逻辑。这个方法首先判断 View 所持有的 mAttachInfo ,当 mAttachInfo 不为空时,直接返回 mAttachInfo 中的 ViewTreeObserver 对象;否则去判断 View 类的 ViewTreeObserver 对象 mFloatingTreeObserver,若 mFloatingTreeObserver 为空,则创建该对象,并返回。

对于 mFloatingTreeObserver,官方注释为:

Special tree observer used when mAttachInfo is null.

很明显,这是一个特殊的视图树观察者对象,只有当 mAttachInfo 为空时才会被使用。

至此,我们成功的获取了 ViewTreeObserver 对象。
4. 确保 ViewTreeObserver 可用

在前面,我们成功获取了 ViewTreeObserver 对象,当我们使用该 对象调用 addOnXxxListener 方法监听 View 的某个状态时,该方法总是首先调用 checkIsAlive() 方法,检测 View 的 ViewTreeObserver 对象是否存活(可用)。

为什么要先去检测呢?官方文档给出的解释是:通过 View 的 getViewTreeObserver() 方法返回的 ViewTreeObserver ,在 View 的生命周期中不能保证始终有效。

既然我如此,那么通过源码我们看一下 checkIsAlive() 都做了什么:

checkIsAlive

逻辑还是很简单的:如果 View 的 ViewTreeObserver 对象不可用,将抛出 IllegalStateException (非法状态异常),并提示我们 重新调用 View 的 getViewTreeObserver() 方法获取对象。

但是,我们能不能在调用 addOnXxxListener 之前,能否检测当前 View 的 ViewTreeObserver 对象是否可用呢,总不能每次等异常了才发现要去重新获取把?答案是肯定的!

ViewTreeObserver 为我们提供了 isAlive() 方法,逻辑很简单,就一句代码“return mAlive”,mAlive 就是在 checkIsAlive() 方法中所判断的变量。该变量标记当前 View 的 ViewTreeObserver 对象是否可用。

最后,翻译官方文档的注释,来总结如何确保 ViewTreeObserver 可用:

当 ViewTreeObserver 不可用时,调用 isAlive() 方法以外的任何其他方法,都将抛出异常。所以,如果 View 对 ViewTreeObserver 持有长时间引用,那么其应该在调用 ViewTreeObserver 对象的任何其他方法之前,确保通过 isAlive() 的返回值检查 ViewTreeObserver 的状态是否可用。
5. 结尾

至此,有关 ViewTreeObserver 的基础只是介绍就到这儿了,相信你一定会觉得没看过瘾,因为本篇只是介绍了 ViewTreeObserver 的概念、如何获取View实例,以及如何避免 ViewTreeObserver 对象为空,我也觉的说的有点简单了。但是,当你发现这些网上都没有,需要靠自己读源码和注释去了解时,就不是那么简单了。

下篇预告:ViewTreeObserver 的 API 介绍、观察者模式是如何起作用的、View 涉及的消息传递机制 以及 ViewTreeObserver 的使用等,统统放出来。请耐心等候,这期间不妨自己去看看源码。

最后,感谢你耐心看完,看完之后有任何问题,欢迎随时交流。

解析 ViewTreeObserver 源码(上)的更多相关文章

  1. 解析 ViewTreeObserver 源码(下)

    继上篇内容,本文介绍 ViewTreeObserver 的使用,以及体会其所涉及的观察者模式,期间会附带回顾一些基础知识.最后,我们简单聊一下 Android 的消息传递,附高清示意图,轻松捋清整个传 ...

  2. Python解析器源码加密系列之(二):一次使用标准c的FILE*访问内存块的尝试

    摘要:由于近期打算修改Python解释器以实现pyc文件的加密/解密,出于保密的要求,解密之后的数据只能放在内存中,不能写入到文件中.但是后续的解析pyc文件的代码又只能接受FILE*作为入参,所以就 ...

  3. HtmlAgilityPack --解析Html源码

    最近项目需要从网络上抓取一下数据解析Html源码,奈何正则表达式难写,于是网上搜索找到了“ HtmlAgilityPack”类库,敏捷开发,果然效率非同寻常. 在此做笔记,写下心得,顺便给自己总结一下 ...

  4. 浩哥解析MyBatis源码(十)——Type类型模块之类型处理器

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6715063.html 1.回顾 之前的两篇分别解析了类型别名注册器和类型处理器注册器,此二 ...

  5. Jsoup解析网页源码时常用的Element(s)类

    Jsoup解析网页源码时常用的Element(s)类 一.简介 该类是Node的直接子类,同样实现了可克隆接口.类声明:public class Element extends Node 它表示由一个 ...

  6. 二十三、并发编程之深入解析Condition源码

    二十三.并发编程之深入解析Condition源码   一.Condition简介 1.Object的wait和notify/notifyAll方法与Condition区别 任何一个java对象都继承于 ...

  7. Thinkphp6源码分析之解析,Thinkphp6路由,Thinkphp6路由源码解析,Thinkphp6请求流程解析,Thinkphp6源码

    Thinkphp6源码解析之分析 路由篇-请求流程 0x00 前言: 第一次写这么长的博客,所以可能排版啊,分析啊,什么的可能会比较乱.但是我大致的流程已经觉得是说的够清楚了.几乎是每行源码上都有注释 ...

  8. Django(49)drf解析模块源码分析

    前言 上一篇分析了请求模块的源码,如下: def initialize_request(self, request, *args, **kwargs): """ Retu ...

  9. 在Activiti官方源码上提交的两个bugfix

    前段时间在Activiti官方源码上提交了两个bugfix,截图为证. 第1个是BPMN model输出的bug:

随机推荐

  1. net core体系-web应用程序-4net core2.0大白话带你入门-6asp.net core配置文件

    asp.net core配置文件   读取配置文件 asp.net core使用appsettings.json代替传统.net framework的web.config中的<appSettin ...

  2. F(x) 数位dp

    Problem Description For a decimal number x with n digits (AnAn-1An-2 ... A2A1), we define its weight ...

  3. window.location.replace和window.location.href区别

    有3个页面 a,b,c 如果当前页面是c页面,并且c页面是这样跳转过来的:a->b->c 1:b->c 是通过window.location.replace("..xx/c ...

  4. spring mvc中的service和controller中读取不到properties值

    根据web.xml读取配置文件中的顺序来看 controller层和service层来自于spring mvc.xml中读取,所以必须要在spring mvc.xml中配置读取资源文件夹方式

  5. vue回调函数无法更改model的值

    data:{ isUpload:true, } 点击上传函数: getFile(event) { //选择图片 let eventId = event.target.id; let type= tes ...

  6. tp5的路由

    路由模式:普通.强制和混合 普通模式: //配置文件关闭路由,完全使用默认的PATH_INFO方式URL 'url_route_on' => false, 关闭路由后的普通模式任然可以通过操作方 ...

  7. Scala-Unit7-Scala并发编程模型AKKA

    一.Akka简介 Akka时spark的底层通信框架,Hadoop的底层通信框架时rpc. 并发的程序编写很难,但是Akka解决了spark的这个问题. Akka构建在JVM平台上,是一种高并发.分布 ...

  8. Java实现Windows、Mouse监听器

    1.通过实现WindowListener接口来实现Windows监听器: import java.awt.event.WindowEvent; import java.awt.event.Window ...

  9. JavaEE 之 Spring Data JPA(二)

    1.JPQL a.定义:Java持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将SQL语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成 ...

  10. TF:TF下CNN实现mnist数据集预测 96%采用placeholder用法+2层C及其max_pool法+隐藏层dropout法+输出层softmax法+目标函数cross_entropy法+AdamOptimizer算法

    import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data # number 1 to 10 ...