Android 中的设计模式:观察者模式

栏目: Android · 发布时间: 6年前

内容简介:观察者模式是我们开发工作中经常使用的开发模式。Android 源码中也有很多地方用到此模式。比如:ListView、ContentProvider 和 Broadcast 等等。本文将会介绍观察者模式、实现一个观察者模式并结合 Android 源码进行分析。定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。观察者模式又被称作发布/订阅模式。即 1 人发布,N 人订阅。观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间者依赖关系很小,甚至不存在依
Android 中的设计模式:观察者模式

前言

观察者模式是我们开发工作中经常使用的开发模式。Android 源码中也有很多地方用到此模式。比如:ListView、ContentProvider 和 Broadcast 等等。本文将会介绍观察者模式、实现一个观察者模式并结合 Android 源码进行分析。

定义

定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。

介绍

观察者模式又被称作发布/订阅模式。即 1 人发布,N 人订阅。观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间者依赖关系很小,甚至不存在依赖。它最用的地方是 GUI 系统、订阅——发布系统。举个例子,我司开发的一个 App 左上角状态栏需要实时显示闹钟的状态,若存在设定好的闹钟则显示闹钟图标,否则不显示。因为存储闹钟使用的 ContentProvider,所以可以使用一个监听器监听闹钟数据是否发生变化,若发生变化就判断当前闹钟状态并显示或关闭图标。这样便实现了实时显示闹钟状态的功能。

UML 类图

Android 中的设计模式:观察者模式

手工实现

  • 创建观察者

实现抽象观察者 Observer 中的方法,这里创建一个 Coder 类,并定义收到通知后的动作:

/**
 * 程序员是观察者
 *
 */
public class Coder implements Observer{
	public String name;
	
	public Coder(String mName) {
		name = mName;
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		// TODO Auto-generated method stub
		System.out.println("嘿," + name + ",您的快递到了");
	}
	
	public String toString(String name) {
		return "程序员:"+name;
	}
}
复制代码
  • 创建被观察者 实现 Observable 方法,也就是快递员,快递到了会通知 程序员 们来拿快递:
/**
 * Courier 即快递员,当他到公司时通知所有观察者(有快递的程序员)拿快递
 * @author Rickon
 *
 */
public class Courier extends Observable {
	public void postNewExpress(String content) {
		//标识状态或者内容发生改变(此处为快递到了)
		setChanged();
		
		//通知所有观察者
		notifyObservers(content);
	}
}
复制代码
  • 测试
public class Test {

	public static void main(String[] args) {
		//被观察者
		Courier courier = new Courier();
		//观察者
		Coder coder1 = new Coder("程序员张三");
		Coder coder2 = new Coder("程序员李四");
		Coder coder3 = new Coder("程序员王二麻子");
		
		//将观察者注册到被观察者的观察者列表中
		courier.addObserver(coder1);
		courier.addObserver(coder2);
		courier.addObserver(coder3);
		
		//快递到了
		courier.postNewExpress("快递到啦!");
	}

}
复制代码
  • 输出结果
嘿,程序员王二麻子,您的快递到了
嘿,程序员李四,您的快递到了
嘿,程序员张三,您的快递到了
复制代码

总结上述代码就实现了一个简单地观察者模式,使用的是 JDK 内部内置的 Observable(抽象被观察者),Observer(抽象观察者)。JDK 内置观察者模式也说明了观察者模式应用的广泛与重要性。

应用场景

  • 事件多级触发场景;
  • 当一个对象必须通知别的对象,而它又不能假定对象是谁;
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优缺点

优点

  • 观察者与被观察者之间是抽象耦合,可以很好地应对业务变化;
  • 增强系统灵活性、可扩展性。

缺点

  • 开发过程中可能会出现一个被观察者、多个观察者的情况,开发和调试会变得比较复杂。除此之外,观察者太多的话收到通知需要消耗更长的时间。

Android 中的观察者模式

  • ListView 深入解析

    ListView 是 Android 中很常用的控件,我们在使用 ListView 添加数据后会调用 Adapter 的 notifyDataSetChanged()方法,这是为啥呢?下面我们来一探究竟!

我们发现 notifyDataSetChanged,这个方法是在 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);
    }
    
    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

    //省略部分代码
}
复制代码

BaseAdapter 看起来是观察者模式,那么到底 BaseAdapter 是怎么运行的呢?又是如何实现的观察者模式,让我们继续分析。

首先看看 mDataSetObservable.notifyChanged() 方法:

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes {@link DataSetObserver#onChanged} on each observer.
     * Called when the contents of the data set have changed.  The recipient
     * will obtain the new contents the next time it queries the data set.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 遍历调用每个观察者的 onChanged() 方法来通知被观察者发生变化
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
    
    //省略部分代码
}

复制代码

这里很容易看到此方法是遍历所有观察者,并且调用所有观察者的 onChnged() 方法,从而告知观察者发生了变化。那么这些观察者是怎么设置的呢?其实这些观察者就是 ListVIew 通过 setAdapter 方法产生的,我们看看这部分代码:

@Override
    public void setAdapter(ListAdapter adapter) {
        //如果已经有了 Adapter,那么先注销 Adapter 对应的观察者
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        //省略部分代码
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            //获取数据的数量
            mItemCount = mAdapter.getCount();
            checkFocus();
            //此处创建一个个数据集观察者
            mDataSetObserver = new AdapterDataSetObserver();
            //将观察者注册到 Adapter 中,实际上是注册到 DatasetObservable
            mAdapter.registerDataSetObserver(mDataSetObserver);

            //省略部分代码
        } else {
           //省略部分代码
        }

        requestLayout();
    }
复制代码

从上述代码中可以看出,在设置 Adapter 时会构建一个 AdapterDataSetObserver,也就是观察者,之后再将这个观察者注册到 Adapter 中,这样我们的被观察者和观察者就都构建好了。那么 AdapterDataSetObserver 到底是怎么运行的呢?它是定义在 ListView 的父类 AbsListView 中,代码如下:

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();
            }
        }
    }
复制代码

它又继承自 AbsListView 的父类 AdapterView 的 AdapterDataSetObserver,代码如下

class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        //前面提到的调用 Adapter 的 notifyDataSetChanged 时会调用所有观察者的 onChanged 方法,核心实现代码就在这里
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            //获取 Adapter 中数据的数量
            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();
            //刷新 ListView、GridView 等 AdapterView 等组件
            requestLayout();
        }
        
        //省略部分代码
    }
复制代码

源码分析到这里就很明显了,当我们调用 Adapter 的 notifyDataSetChanged 方法,这个方法又会调用 DataSetObservable 的 notifyDataSetChanged 方法,而这个方法会调用所有观察者的 onChanged 方法,在 onChanged 方法中则是会调用 ListView 的重新布局方法刷新UI,这就是一个完整的观察者模式。

ListView 观察者模式实现总结:我重新概述这一过程:Adapter 中包含一个数据集可观察者 DataSetObservable,在数据数量发生变更时,开发者手动调用 Adapter.notifyDataSetChanged,实际上调用的则是 DataSetObservable 的 onChanged 方法,该方法会遍历所有观察者的 onChanged 方法。在 AdapterDataSetObserver 的 onChanged 函数中会获取 Adapter 中数据集的新数量,然后调用 ListView 的 requestLayout() 方法重新布局并更新 UI。

  • ContentProvider 文章的开始有提到使用 ContentProvider 的过程中也用到了观察者模式。首先创建一个观察者对象,代码如下:
ContentObserver alarmObserver = new ContentObserver(new Handler()) {
        //创建观察者对象
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            //执行业务代码
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
        }
    };
复制代码

注册观察者:

mContext.getContentResolver().registerContentObserver(url, true, alarmObserver);
复制代码

看到此注册方法有三个参数分别代表什么意义呢:

  • uri:针对对有变化的感兴趣进行监听的URI;
  • notifyForDescendents:true表示以uri前缀开始的任何变化都进行通知;false表示完全匹配才进行通知;
  • observer:IContentObserver接口,提供了一个方法onChange,变化发生时Cursor需要更新时调用

这样就通过指定Uri可以仅对数据库中感兴趣的数据有变化时,进行监听。具体源码就不再进行分析,大家感兴趣可以自行去分析源码,观察者模式是万变不离其宗的。


以上所述就是小编给大家介绍的《Android 中的设计模式:观察者模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cascading Style Sheets 2.0 Programmer's Reference

Cascading Style Sheets 2.0 Programmer's Reference

Eric A. Meyer / McGraw-Hill Osborne Media / 2001-03-20 / USD 19.99

The most authoritative quick reference available for CSS programmers. This handy resource gives you programming essentials at your fingertips, including all the new tags and features in CSS 2.0. You'l......一起来看看 《Cascading Style Sheets 2.0 Programmer's Reference》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器