分享

ListView中的观察者模式

 个人文档awpyia 2017-05-10

今日科技快讯

近日,苏宁云商公告,公司全资子公司南京苏宁易购与关联方阿里巴巴中国拟共同出资10亿元设立重庆猫宁电子商务有限公司(暂定名,简称“猫宁电商”)。其中,南京苏宁易购出资5.1亿元,持有猫宁电商51%的股权,阿里巴巴中国出资4.9亿元,持有猫宁电商49%的股权。双方在保持业务独立性的基础上协同各自在用户、平台、商品、服务、技术等方面的资源,服务消费者、激能品牌商、赋能中小零售商,创新变革零售行业。

作者简介

本篇来自 无比耿直的程序猿 的投稿,直接开撸关键的源码,带你了观察者模式在ListView中的应用。

无比耿直的程序猿 的博客地址:

http://blog.csdn.net/xyh269

正文

虽然现在 RecyclerView 很好用,也在逐渐替代 ListView。很多github的开源大神也在对其进行更加实用的封装。我现在写的一个音乐播放器也在使用 RecyclerView。但是这些都不阻碍我们学习 ListView 优秀的源码设计。

进入正题,我用的是 Api-23 的源码。接下来就从源码的角度带你学习 ListView 中的观察者模式。

当我们开启异步线程,向服务端拉取数据后,数据源已经更新了,此时想要更新 ListView 的视图以显示新的数据。

ListView 使用了 Adapter模式,很简单只需一行代码就能完成 ListView 的更新。

mAdapter.notifyDataSetChanged();

那么这里引出一个问题:

更新ListView 的工作,是 Adapter完成的 还是 ListView自身内部完成的?可以先猜想一下再往下看。

因为我之前已经学习过自定义控件,所以我看源码之前猜想是 ListView 完成的。惯性使然,我想到他可能是调用了 onLayout()onDraw() 等方法呀,去重新布局,绘制。

那接下来就解开疑惑吧。

先找到源头,从 ListView绑定Adapter 那里开始:

mListView.setAdapter(mAdapter);

ListView Adapter 就是用这行代码建立起关联的。

那么跟踪 setAdapter 方法进去:


方法是这样开始的:

if (mAdapter != null && mDataSetObserver != null) {    mAdapter.unregisterDataSetObserver(mDataSetObserver);}

先判断 mAdapter != null && mDataSetObserver != null

mAdapter 肯定是 不为null 的,那么 mDataSetObserver 呢?这个引用是哪里被赋值的,先不管,继续往下看 setAdapter 方法。

这里先分享我看源码的方法吧:

刚开始的时候我是很喜欢往深处闯,导致看了一天都无法自拔,思路又散了。

现在我看源码都是挑重点看,比如这个 setAdapter 方法,一路看下来都没有 return 语句跳出,那么就一定会来到 if(mAdapter !=null )这个判断,如下:


到了这里,我们也就找到了 mDataSetObserver,原来是在这里被赋值的。现在得出小结论:

  • 在 ListView 的 setAdapter方法 中,生成了一个 AdapterDataSetObserver 对象并赋值给 mDataSetObserver

  • 调用 Adapter 的 registerDataSetObserver 方法将 mDataSetObserver 注册进去。

现在我们好奇的是 Adapter 的 registerDataSetObserver 方法。继续前进。

BaseAdapter 类中找到了 registerDataSetObserver 方法,并且也找到了经常调用的,很熟悉的 notifyDataSetChanged 方法。如下:


可以看到,在 registerDataSetObserver 方法中,又调用了 DataSetObservable registerObserver 方法将传进来的 AdapterDataSetObserver 对象注册进去,那么这个 DataSetObservable 又是什么呢?继续跟进。

这个 DataSetObservable 源码比较少,那就全部贴出:


好像看不太懂。mObservers 是什么?竟然没有 registerObserver 方法。哈哈,那肯定是父类继承下来的啊。在 DataSetObservable 类中暂时没我们想要知道的信息,那么就看看他的父类 Observable 吧。Observable 还是个泛型。不管,看内部实现原理就好:


找到了 registerObserver 方法。代码逻辑还挺简单的。我们又可以得出小结论:

DataSetObservable 的内部维护着一个观察者集合,即源码中的 mObservers。当我们的 ListView 绑定了 Adapter,调用 BaseAdapter 的 registerDataSetObserver 方法时,实际上是在这个观察者集合 mObservers 里将该观察者添加进来。对 ListView 来说,这个观察者就是 AdapterDataSetObserver

完成注册。以上就是 setAdapter 方法的源码分析。

再看到 BaseAdapter的notifyDataSetChanged() 方法:

public void notifyDataSetChanged() {    mDataSetObservable.notifyChanged();}

内部调用了 DataSetObservable notifyChanged 方法。

再回到 DataSetObservable 的源码,看到 notifyChanged() 方法:


从观察者集合里遍历出观察者,并调用该观察者的 onChange() 方法,很清楚了吧。

当我们调用 Adapter notifyDataSetChanged 方法 更新ListView

notifyDataSetChanged 方法中又会调用 DataSetObservable notifyChanged 方法。

而从 DataSetObservable 的源码中,我们知道了在 notifyChanged 方法中又会遍历出 AdapterDataSetObserver(观察者),并调用这个观察者的 onChanged() 方法。

完毕,底层实现就是这样。

接下来只需要知道 AdapterDataSetObserver(观察者)的 onChanged() 方法里做了什么就好了。

AdapterDataSetObserver,是 ListView 的父类 AdapterView 的一个内部类。他是真的有 onChanged 方法的。不信你看:


终于揭开谜底,在 AdapterDataSetObserver onChanged() 方法里,实际上是调用了 View requestLayout() 方法进行重新策略,布局,绘制整个 ListView 子项item view

requestLayout() 的源码如下:


AdapterView 是继承 ViewGroup 的,但是 ViewGroup 并没有重写 requestLayout() 方法。有能力的同学可以继续深入研究 AdapterView 到底是怎么重新布局的。

至此,我们已经解开了开篇的疑惑。

综上所述,AdapterDataSetObserver 这个是观察者,在 AdapterDataSetObserver onChanged 函数中,实际上调用的是 View 中的方法完成了整个 更新ListView 的工作,AdapterDataSetObserver 只是在外层进行了包装,真正的核心功能是 ListView,更加准确的说话是 ListView 的父类 AdapterView

ListView 就是通过 Adapter模式观察者模式子项复用机制 实现了视图良好的扩展性,节约了内存开销,提高了运行效率。

更多


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多