Android设计模式之——观察者模式

观察者模式最常用的地方是GUI(图形用户界面)系统、订阅——发布系统。其重要作用就是将观察者和被观察者“解耦”,尽可能的降低两者之间的依赖性。

观察者模式定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。它是一种行为型模式,行为型模式主要是描述类或者对象是怎样交互和怎样分配职责的。

使用场景

  • 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系;
  • 事件多级触发场景;
  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

角色介绍

  • Subject:抽象主题,也就是被观察(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫具体被观察者(ConcreteObservable)角色。
  • Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。
  • ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。

观察者模式的简单实现

1.抽象观察者

public interface Observer {

    void doSth(String msg); //接收通知的方法
}

 2.具体观察者

/**
 *男人
 */
public class Man implements Observer {

    private String mName;

    public Man(String mName) {
        this.mName = mName;
    }

    @Override
    public void doSth(String msg) { //接收通知后的具体实现方法
        LogUtil.e("Observer--->", "这个男人:" + mName + msg + "的方式是:打了一套军体拳...");
    }
}

/**
 *女人
 */
public class WoMan implements Observer {

    private String mName;

    public WoMan(String mName) {
        this.mName = mName;
    }

    @Override
    public void doSth(String msg) {
        LogUtil.e("Observer--->", "这个女人:" + mName + msg + "的方式是:闭着眼瞎挠...");
    }
}

3.抽象被观察者

分别定义了“添加”、“移除”以及“通知”观察者的方法

public interface Observable {

    void add(Observer observer);

    void remove(Observer observer);

    void notify(String string);
}

4.具体被观察者

public class SuperMan implements Observable {

    //观察者集合
    private List<Observer> mList = new ArrayList<>();

    @Override
    public void add(Observer observer) {
        mList.add(observer);
    }

    @Override
    public void remove(Observer observer) {
        mList.remove(observer);
    }

    @Override
    public void notify(String string) {
        for (Observer observer1:mList){
            observer1.doSth(string);
        }
    }
}

5.测试

...
...
public void test(){

    SuperMan superMan = new SuperMan();
    superMan.add(new Man("张三"));
    superMan.add(new WoMan("Mary"));

    superMan.add(new Observer() { //有没有感觉类似于new OnClickListener()
            @Override
            public void doSth(String msg) {
                LogUtil.e("Observer--->", "让我们一起摇摆!!!");
            }
        });    

    superMan.notify("打坏蛋");
}
...
...

输出结果

这个男人:张三打坏蛋的方式是:打了一套军体拳...
这个女人:Mary打坏蛋的方式是:闭着眼瞎挠...
让我们一起摇摆!!!

以上就是观察者模式的一个小测试,下面我们来看看Android开发中使用到的观察者模式

1.控件的点击监听Listener

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //TODO
            }
        });

button就是一个被观察者,new View.OnClickListener()就是一个观察者,OnClickListener是个接口,即抽象观察者。被观察者button通过setOnClickListener()把观察者<new View.OnClickListener()>添加到自己名下。

观察者<new View.OnClickListener()>通过观察button的状态变化来回调实现了OnClickListener接口下的onClick方法,从而做出相应的操作。感兴趣的同学可以去研究一下View的源码。

2.Adapter的notifyDataSetChanged()方法

咱们把鼠标放在adapter.notifyDataSetChanged();方法上,然后CTRL+鼠标左键点进去瞅一眼...

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.
     * (通知已经依附的观察者们基础数据已经改变,并且任何反映数据集的View都应该刷新他自己)
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

 观察者模式无疑,BaseAdapter是个被观察者。现在咱把鼠标放在mDataSetObservable.notifyChanged();方法上并重复上一步的操作来深挖一下这个通知:

/**
 * A specialization of {@link Observable} for {@link DataSetObserver}
 * that provides methods for sending notifications to a list of
 * {@link DataSetObserver} objects.
 *
 * 译:为DataSetObserver提供给一个Object集合发送通知的方法的一个专业化的Observable
 */
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.
     *
     * 译:在每个Observer中调用DataSetObserver的onChanged方法。
     *     当数据集的内容已更改时调用,接收者将在下一次查询数据集时获取新内容。
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // since onChanged() is implemented by the app, it could do anything, 
            // including
            // 一旦onChanged()方法被执行,它可以做任何事,包括
            // removing itself from {@link mObservers} - and that could cause problems if
            // 从 mObservers移除自己,如果这个mObservers集合使用了一个迭代器会引发问题。
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            // 为了避免这样的问题,就按相反的顺序浏览列表
            for (int i = mObservers.size() - 1; i >= 0; i--) { //遍历所有的观察者,调用onChanged()方法
                mObservers.get(i).onChanged();
            }
        }
    }

    ...
    ...
}

在上面的代码中我们发现了"mObservers"观察者,它在Observable中

public abstract class Observable<T> {
    /**
     * The list of observers.  An observer can be in the list at most
     * once and will never be null.
     * 译:观察者集合,一个观察者最多可以在这个集合中一次并且永不为空。
     */
    protected final ArrayList<T> mObservers = new ArrayList<T>();

    /**
     * Adds an observer to the list. The observer cannot be null and it must not already
     * be registered.
     * 在集合中添加一个观察者,这个观察者不能为空并且必须没有被注册
     */
    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);
        }
    }

    /**
     * Removes a previously registered observer. The observer must not be null and it
     * must already have been registered.
     * 移除前一个注册的观察者,这个观察者不能为空并且必须是已经被注册的
     */
    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();
        }
    }
}

跟我们上面写的例子差不多,只不过方法内部添加了一些异常判断。

下面就看看观察者是如何被注册到被观察者中的:

public class ListView extends AbsListView {
   
    ...
    ...
    
    public void setAdapter(ListAdapter 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();
            //注册观察者
            mAdapter.registerDataSetObserver(mDataSetObserver);

           ...
           ...
        } 
    }
}

再让我们多看一眼这个观察者AdapterDataSetObserver里究竟干了些啥...

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

       ...
       ...
    }

......就有一个简单的onChanged()方法的复写,好像啥也看不出来,那就试试从它爹那找找看吧......

class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            //重新布局
            requestLayout();
        }

       ...
       ...
    }

这下看到了onChanged()方法的精髓,前面呜里巴嗦一大堆,我不管,也看不见,我就看到了requestLayout();布局更新。

看了这么多源码可能会有一些疲劳,下面给大家简单做个总结:当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged方法,这个方法又会调用DataSetObservable的notifyChanged方法,这个方法又遍历了所有的观察者(AdapterDataSetObserver )并逐一调用其onChanged方法,在onChanged方法中又会调用ListView的requestLayout方法更新布局。

过程大致如下:

1.在setAdapter的时候会初始化一个AdapterDataSetObserver的实例,并注册到Adapter中,这是个观察者;

2.在数据发生改变时,我们会调用Adapter.notifyDataSetChanged方法,实际上是调用BaseAdapter中DataSetObservable的notifyChanged方法,该方法遍历所有的观察者并调用其onChanged方法;

3.在AdapterDataSetObserver的onChanged方法中会获取Adapter中数据集的新数量,然后调用ListView的requestLayout方法更新布局。

其他使用观察者模式的还有BroadcastReceiver,以及我们熟悉的第三方库EventBus、RxJava等,这里由于篇幅已经够长就不多做分析了,有机会单独拿出来学习一下里面的原理。

猜你喜欢

转载自blog.csdn.net/S_Alics/article/details/97237734