热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在BaseAdapter源码中了解观察者模式

转载请标明出处:http:blog.csdn.nethai_qing_xu_kongarticledetails76146635本文出自:【顾林海的博客】

转载请标明出处:
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 {

void update(T news);

}



创建观察者(Email)具体实现类:

public class Email implements EmailObserver<NewsBean> {

private String user;

public Email(String user) {
this.user = user;
}

@Override
public void update(NewsBean news) {
System.out.println("尊敬的" + user + "用户,收到一封新闻邮件,标题为:" + news.title);
}

}



创建被观察者(新闻网站)抽象类:

public abstract class NewObservable {

private List 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 mObservers = new ArrayList();

/**
* 添加注册一个观察者到观察者列表中。
*/

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)已经分析完毕,这里总结一些知识点:

  1. BaseAdapter内部绑定了一个被观察者的数据集的类(DataSetObservable)。
  2. DataSetObservable内部实现了两个通知观察者的方法,分别是notifyChanged()和notifyInvalidated()方法。
  3. DataSetObservable继承自Observable泛型类,具体观察者对象是DataSetObserver(ListView内部实现类)。
  4. 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 selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}

requestLayout();
}



在setAdapter一开始就会去判断观察者是否存在,如果已经存在就会取消注册,在(2)中,会创建观察者,并在(3)中添加到被观察者中,AdapterDataSetObserver定义在ListView父类中。



AbsListView内部类AdapterDataSetObserver:

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}

@Override
public 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;

@Override
public 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();
}

@Override
public 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 restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}

// Data is invalid so we should reset our state
mOldItemCount = 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)已经分析完毕,这里总结一些知识点:

  1. 通过给setAdapter(adapter)方法进行观察者的注册。
  2. 被观察者通过notifyChanged()方法通知观察者(AbsListView内部类 AdapterDataSetObserver中的 onChanged()和onInvalidated()方法 )进行处理。
  3. AdapterDataSetObserver继承自AdapterView内部类AdapterDataSetObserver,并交由它来进行AdapterView组件的重新布局。

推荐阅读
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 如何高效解决Android应用ANR问题?
    本文介绍了ANR(应用程序无响应)的基本概念、常见原因及其解决方案,并提供了实用的工具和技巧帮助开发者快速定位和解决ANR问题,提高应用的用户体验。 ... [详细]
  • 管理UINavigationController中的手势返回 - Managing Swipe Back Gestures in UINavigationController
    本文介绍了如何在一个简单的闪存卡片应用中实现平滑的手势返回功能,以增强用户体验。 ... [详细]
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 问题场景用Java进行web开发过程当中,当遇到很多很多个字段的实体时,最苦恼的莫过于编辑字段的查看和修改界面,发现2个页面存在很多重复信息,能不能写一遍?有没有轮子用都不如自己造。解决方式笔者根据自 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
  • Logging all MySQL queries into the Slow Log
    MySQLoptionallylogsslowqueriesintotheSlowQueryLog–orjustSlowLog,asfriendscallit.However,Thereareseveralreasonstologallqueries.Thislistisnotexhaustive:Belowyoucanfindthevariablestochange,astheyshouldbewritteninth ... [详细]
  • 本文介绍了如何在两个Oracle数据库(假设为数据库A和数据库B)之间设置DBLink,以便能够从数据库A中直接访问和操作数据库B中的数据。文章详细描述了创建DBLink前的必要准备步骤以及具体的创建方法。 ... [详细]
  • 在Qt框架中,信号与槽机制是一种独特的组件间通信方式。本文探讨了这一机制相较于传统的C风格回调函数所具有的优势,并分析了其潜在的不足之处。 ... [详细]
  • 本文将深入探讨 Unreal Engine 4 (UE4) 中的距离场技术,包括其原理、实现细节以及在渲染中的应用。距离场技术在现代游戏引擎中用于提高光照和阴影的效果,尤其是在处理复杂几何形状时。文章将结合具体代码示例,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 在尝试通过自定义端口部署Spring Cloud Eureka时遇到了连接失败的问题。本文详细描述了问题的现象,并提供了有效的解决方案,以帮助遇到类似情况的开发者。 ... [详细]
  • 本文探讨了一种统一的语义数据模型,旨在支持物联网、建筑及企业环境下的数据转换。该模型强调简洁性和可扩展性,以促进不同行业间的插件化和互操作性。对于智能硬件开发者而言,这一模型提供了重要的参考价值。 ... [详细]
author-avatar
Saber木木-汐7rv
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有