Android之在BaseAdapter源码中了解观察者模式
转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/76146635
本文出自:【顾林海的博客】
个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!
前言
观察者模式也叫发布订阅模式,它是定义如下:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。适用的适用场景主要有关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系;事件多级触发场景;跨系统的消息交互场景,如消息队列、事件总线的处理机制。比如现在非常主流的EventBus, RxJava等框架就是采用的观察者模式。
类图说明
Subject被观察者,定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
Observer观察者,观察者接受到消息后,即更新操作,对接收到的信息进行处理。
ConcreteSubject具体的被观察者,定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
ConcreteObserver具体的观察者,每个观察在接收到消息后的处理反应是不同,各个观察者自己的处理逻辑。
日常案例
在日常中比如订阅新闻邮件就是一种典型的观察者模式,用户邮箱是观察者,新闻网站是被观察者,一旦新闻网站有新闻更新,会通知用户并给用户邮箱发送新闻。
创建新闻的实体类NewsBean:
/*** 新闻实体类*/
public class NewsBean {public String title;public String info;
}
创建观察者(Email)接口:
public interface EmailObserver<T> {void update(T news);}
创建观察者(Email)具体实现类:
public class Email implements EmailObserver<NewsBean> {private String user;public Email(String user) {this.user = user;}@Overridepublic void update(NewsBean news) {System.out.println("尊敬的" + user + "用户,收到一封新闻邮件,标题为:" + news.title);}}
创建被观察者(新闻网站)抽象类:
public abstract class NewObservable<T> {private List<EmailObserver> observers = new ArrayList<>();public void registerObserver(EmailObserver observer) {if (!observers.contains(observer)) {observers.add(observer);}}public void unregisterObserver(EmailObserver observer) {if (observers.contains(observer)) {observers.remove(observer);}}public void notifyObserver(T t) {for (EmailObserver emailObserver : observers) {emailObserver.update(t);}}public void clearAllObserver() {observers.clear();}}
NewObservable类中提供了观察者的添加、取消以及通知操作的实现。
创建被观察者(新闻网站)的具体实现:
public class NewService extends NewObservable<NewsBean> {public void make() {NewsBean newsBean = new NewsBean();newsBean.title = "号外,号位,某某程序员居然找到女朋友了";newsBean.info = "新闻内容.....";notifyObserver(newsBean);}
最后在场景类中使用:
public class Client {public static final void main(String[] args) {//创建被观察者新闻网站NewService newService = new NewService();//创建一些观察者用户Email user1 = new Email("用户1");Email user2 = new Email("用户2");Email user3 = new Email("用户3");//将观察者添加到被观察中newService.registerObserver(user1);newService.registerObserver(user2);newService.registerObserver(user3);//被观察者(网站)发布一则新闻newService.make();}
}
运行结果:
尊敬的用户1用户,收到一封新闻邮件,标题为:号外,号位,某某程序员居然找到女朋友了
尊敬的用户2用户,收到一封新闻邮件,标题为:号外,号位,某某程序员居然找到女朋友了
尊敬的用户3用户,收到一封新闻邮件,标题为:号外,号位,某某程序员居然找到女朋友了
Android源码中观察者模式的使用
在平时开发中,ListView用的比较多,可以通过setAdapter(adapter)方法给ListView设置适配器,其中的BaseAdapter就是实现了观察者模式。
被观察者(BaseAdapter)
BaseAdapter 源码:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {/*数据集观察者*/private final DataSetObservable mDataSetObservable = new DataSetObservable();public boolean hasStableIds() {return false;}/*** 进行观察者的注册*/public void registerDataSetObserver(DataSetObserver observer) {mDataSetObservable.registerObserver(observer);}/*** 该观察者的取消注册*/public void unregisterDataSetObserver(DataSetObserver observer) {mDataSetObservable.unregisterObserver(observer);}/*** 通知观察者(ListView重绘当前可见区域)*/public void notifyDataSetChanged() {mDataSetObservable.notifyChanged();}/*** 通知观察者(ListView会重绘控件)*/public void notifyDataSetInvalidated() {mDataSetObservable.notifyInvalidated();}}
查看BaseAdapter会发现它就是一个观察者模式,其中的DataSetObservable是一个数据集的观察者,并且在BaseAdapter源码中实现了被观察者职责方法:动态地增加、取消观察者,管理观察者并通知观察者。其中notifyDataSetChanged()方法是用于通知ListView重绘当前的可见区域,notifyDataSetInvalidated()方法用于通知ListView重绘控件。
DataSetObservable承载这被观察者的职责,查看DataSetObservable源码:
public class DataSetObservable extends Observable<DataSetObserver> {/*** 同步操作,通知观察者(这里是ListView)*/public void notifyChanged() {synchronized(mObservers) {/*** 遍历观察者,并通知*/for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onChanged();}}}/*** 同步操作,通知观察者(这里是ListView)*/public void notifyInvalidated() {synchronized (mObservers) {/*** 遍历观察者,并通知*/for (int i = mObservers.size() - 1; i >= 0; i--) {mObservers.get(i).onInvalidated();}}}
}
DataSetObservable内部实现了通知观察者(ListView)的方法,并继承自Observable泛型类,我们继续看Observable的源码,其实到这里大家应该能知道Observable类具体做了哪些操作,一定提供了对观察者的注册和取消注册操作,这里继承Observable泛型类时指定了具体的观察者类型是DataSetObserver,这里先提下DataSetObserver 这个观察者是在ListView某个父类中定义,其实也就很好的说明了ListView是观察者,观察实现了BaseAdapter类中的数据变化,一旦数据产生变化,通过BaseAdapter的notifyChanged()方法通知ListView的重绘。
Observable源码:
public abstract class Observable<T> {/*** 用于保存观察者的列表*/protected final ArrayList<T> mObservers = new ArrayList<T>();/*** 添加注册一个观察者到观察者列表中。*/public void registerObserver(T observer) {if (observer == null) {throw new IllegalArgumentException("The observer is null.");}synchronized(mObservers) {if (mObservers.contains(observer)) {throw new IllegalStateException("Observer " + observer + " is already registered.");}mObservers.add(observer);}}/*** 从列表中移除(取消注册)观察者。*/public void unregisterObserver(T observer) {if (observer == null) {throw new IllegalArgumentException("The observer is null.");}synchronized(mObservers) {int index = mObservers.indexOf(observer);if (index == -1) {throw new IllegalStateException("Observer " + observer + " was not registered.");}mObservers.remove(index);}}/*** 情况所有观察者*/public void unregisterAll() {synchronized(mObservers) {mObservers.clear();}}
}
> Observable内部提供了一个用于保存观察者的列表,并提供了registerObserver(observer)方法用于注册观察到列表中,unregisterObserver(observer)方法用于从观察者列表移除一个观察者,以及unregisterAll()方法清空所有观察者。
到这里整个被观察者(BaseAdatper)已经分析完毕,这里总结一些知识点:
- BaseAdapter内部绑定了一个被观察者的数据集的类(DataSetObservable)。
- DataSetObservable内部实现了两个通知观察者的方法,分别是notifyChanged()和notifyInvalidated()方法。
- DataSetObservable继承自Observable泛型类,具体观察者对象是DataSetObserver(ListView内部实现类)。
- DataSetObservable(被观察者)提供了观察者的注册(存放在列表中)方法、取消注册(从列表中移除)以及清空所有观察者。
观察者(ListView)
在给ListView添加适配器时,会调用ListView的setAdapter(adapter)方法,接下来从ListView的setAdater(adapter)方法的具体实现进行深入:
@Override
public void setAdapter(ListAdapter adapter) {/*** (1)第一次为ListView设置适配器时,如果已经存在观察者,就将它移除。*/if (mAdapter != null && mDataSetObserver != null) {mAdapter.unregisterDataSetObserver(mDataSetObserver);}resetList();mRecycler.clear();if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);} else {mAdapter = adapter;}mOldSelectedPosition = INVALID_POSITION;mOldSelectedRowId = INVALID_ROW_ID;super.setAdapter(adapter);if (mAdapter != null) {mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();mOldItemCount = mItemCount;mItemCount = mAdapter.getCount();checkFocus();/*** (2)创建观察者。*/mDataSetObserver = new AdapterDataSetObserver();/*** (3)将观察者注册到被观察者中。*/mAdapter.registerDataSetObserver(mDataSetObserver);mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());int position;if (mStackFromBottom) {position = lookForSelectablePosition(mItemCount - 1, false);} else {position = lookForSelectablePosition(0, true);}setSelectedPositionInt(position);setNextSelectedPositionInt(position);if (mItemCount == 0) {// Nothing selectedcheckSelectionChanged();}} else {mAreAllItemsSelectable = true;checkFocus();// Nothing selectedcheckSelectionChanged();}requestLayout();
}
在setAdapter一开始就会去判断观察者是否存在,如果已经存在就会取消注册,在(2)中,会创建观察者,并在(3)中添加到被观察者中,AdapterDataSetObserver定义在ListView父类中。
AbsListView内部类AdapterDataSetObserver:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {@Overridepublic void onChanged() {super.onChanged();if (mFastScroll != null) {mFastScroll.onSectionsChanged();}}@Overridepublic void onInvalidated() {super.onInvalidated();if (mFastScroll != null) {mFastScroll.onSectionsChanged();}}
}
回到BaseAdapter中,在调用BaseAdapter的notifyChanged方法时会执行继承自Observable 泛型类的DataSetObservable中的notifyChanged()方法,在这个方法中去遍历父类中的观察者列表,并调用它们的onChanged()和onInvalidated()方法,在上面分析被观察者(BaseAdapter)时,已经知道了观察者的实例类是DataSetObserver,而AbsListView内部类AdapterDataSetObserver继承自 AdapterView内部类AdapterDataSetObserver。
AdapterView内部类AdapterDataSetObserver:
class AdapterDataSetObserver extends DataSetObserver {private Parcelable mInstanceState = null;@Overridepublic void onChanged() {mDataChanged = true;mOldItemCount = mItemCount;mItemCount = getAdapter().getCount();// Detect the case where a cursor that was previously invalidated has// been repopulated with new data.if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null&& mOldItemCount == 0 && mItemCount > 0) {AdapterView.this.onRestoreInstanceState(mInstanceState);mInstanceState = null;} else {rememberSyncState();}checkFocus();requestLayout();}@Overridepublic void onInvalidated() {mDataChanged = true;if (AdapterView.this.getAdapter().hasStableIds()) {// Remember the current state for the case where our hosting activity is being// stopped and later restartedmInstanceState = AdapterView.this.onSaveInstanceState();}// Data is invalid so we should reset our statemOldItemCount = mItemCount;mItemCount = 0;mSelectedPosition = INVALID_POSITION;mSelectedRowId = INVALID_ROW_ID;mNextSelectedPosition = INVALID_POSITION;mNextSelectedRowId = INVALID_ROW_ID;mNeedSync = false;checkFocus();requestLayout();}public void clearSavedState() {mInstanceState = null;}
}
由于AbsListView内部类AdapterDataSetObserver重写了 AdapterView内部类AdapterDataSetObserver的onChanged()和onInvalidated()方法 。也就是说BaseAdapter的notifyChanged最终执行到ListView的父类AbsListView的内部类AdapterDataSetObserver中的onChanged()和onInvalidated()方法,查看这两个方法可以看出它们调用了父类的onChanged()和onInvalidated()方法,可以看出ListView重绘的具体实现是在AdapterView内部类AdapterDataSetObserver类中实现的,在onChanged()和onInvalidated()方法中最终调用requestLayout()方法进行ListView、GridView等AdapterView组件的重新布局。
到这里整个观察者(ListView)已经分析完毕,这里总结一些知识点:
- 通过给setAdapter(adapter)方法进行观察者的注册。
- 被观察者通过notifyChanged()方法通知观察者(AbsListView内部类 AdapterDataSetObserver中的 onChanged()和onInvalidated()方法 )进行处理。
- AdapterDataSetObserver继承自AdapterView内部类AdapterDataSetObserver,并交由它来进行AdapterView组件的重新布局。
Android之在BaseAdapter源码中了解观察者模式相关推荐
- Android直播APP源码中排行榜功能如何实现
刚进公司的时候,听技术人员说起直播APP源码中的"排行榜"功能,小编最先想到的是学生时期的成绩排行,上榜的沾沾自喜到下次考试,下榜的哭哭啼啼,其实就算上榜也并没有什么实质性的奖励, ...
- 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile 构造函数及相关调用函数 | Android 源码中查找 native 函数 )
文章目录 一.DexFile 构造函数 二.DexFile.openInMemoryDexFile 函数 三.Android 源码中查找 native 函数 一.DexFile 构造函数 上一篇博客 ...
- 【Android 内存优化】Android 原生 API 图片压缩原理 ( 图片质量压缩方法 | 查找 Java 源码中的 native 方法对应的 C++ 源码 )
文章目录 一. 图片质量压缩方法 二. 查找对应的 Native 方法源码 三. 分析 Bitmap.cpp 中动态注册 Native 方法 在博客 [Android 内存优化]图片文件压缩 ( An ...
- android 源码中的单例,Android源码中的一种单例实现
单例模式的实现方式有懒汉,饿汉,双重校验锁,枚举,内部类等等,写法就不全部列举了.Android源码中有一个单例辅助类/frameworks/base/core/java/android/util/S ...
- Android源码中的FLAG为何使用16进制
1.在阅读源码的时候经常发现有一些标志属性使用一些位操作来判断是否具有该标志,增加标志或者去除标志. 比如View.java中的 /*** This view does not want keystr ...
- android源码使用方法,android源码中使用到的设计模式(创建型)
1.单例模式 1.1定义 确保某个类只有一个实例,而且自行实例化并向整个系统提供者个实例. 1.2单例的形式 饿汉模式:第一次就加载,用空间换时间. public class SingleTon { ...
- android单例模式代码,设计模式(一):Android 源码中的单例模式
设计模式(一):Android 源码中的单例模式 2020-08-17 22:51 阅读数 57 <>什么是单例模式? 单例模式(Singleton) 是最常见也最简单的设计模式,它的目的 ...
- android 2.3.6Gallary源码导入到Eclipse中编译
android 2.3.6 Gallary源码太out了吧,是的,很out.只是作为整个流程的试试手罢了.我想其他的导入方法也可大致相同罢了.另外,这个代码还是大有看头的,研究研究也是不错. 使用的环 ...
- android 静态工厂方法,Android 源码中的静态工厂方法
我们知道工厂模式有三兄弟,通常我们说的工厂模式指的是工厂方法模式,它的应用频率最高.本篇博客分享的简单工厂模式是工厂方法模式的"小弟",确切的来讲它不属于设计模式,而是一种方法.此 ...
最新文章
- 刨根问底: Kafka 到底会不会丢数据?
- jQuery 之正则表达式篇
- android 回调函数二:应用实例
- linux系统中使用chattr命令的,chattr命令怎么用
- Windows未能启动,原因可能是最近更改了硬件或软件,解决此问题的步骤
- AcWing 312. 乌龟棋
- [发布] 矩阵乘法及其对于编程求斐波那契数列的某一项的应用
- grub rescue的修复方法
- Keras-8 Predicting house prices: a regression example
- SpringCloud+Docker+Jenkins+GitLab+Maven实现自动化构建与部署实战
- 最多只能选择两个多选框的jQuery功能实现
- [树上倍增][最小生成树]JZOJ P4313——电话线铺设
- 用python提取文字中省份与城市
- (附源码)springboot奖助学金评审系统 毕业设计 031035
- 生鲜B2B2C供应链解决方案
- PHP用curl请求网址的code码
- allegro 使用subdrawing
- linux菜刀使用教程,中国菜刀的使用教程
- 自助建站系统哪个好?
- 用户访谈提纲设计注意事项