观察者模式

1、定义

观察者模式就是定义对象间的一种一对多的依赖关系。当被观察者(Observable)发生改变,所有依赖与它的对象(观察者,Observer)都会得到通知并自动更新。

2、分析BaseAdapter

观察者模式是我们常接触的一种设计模式,比如经常使用的ListVIew的BaseAdapter使用的就是观察者模式。


观察者模式_第1张图片
image.png
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {

    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
    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();
    }

    。。。
}

1、通知数据的改变

当数据发生改变的时候,我们通常会调用BaseAdapter的notifyDataSetChanged(),通知所有的观察者:通过遍历所有的观察者(mObservers),并调用其onChanged()来通知观察者。

public class DataSetObservable extends Observable {

    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    。。。
}

但是观察者是从哪里来的呢?

2、注册观察者

这些观察者(Observer)是在setAdapter()的时候创建并注册到被观察者(Observable)的。

public class ListView extends AbsListView {
    /**
     * Should be used by subclasses to listen to changes in the dataset
     */
    AdapterDataSetObserver mDataSetObserver;

    /**
     * The adapter containing the data to be displayed by this view
     */
    ListAdapter mAdapter;
    。。。
    @Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        。。。

        mAdapter = adapter;

        if (mAdapter != null) {

            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);

            。。。
        } 

        requestLayout();
    }
}

其中AdapterDataSetObserver在ListView的父类中,并在onChanged()被调用的时候,调用requestLayout()重新布局listView。

class AdapterDataSetObserver extends DataSetObserver {

        @Override
        public void onChanged() {
            。。。
            requestLayout();
        }

        @Override
        public void onInvalidated() {
            。。。
            requestLayout();
        }
}

所以,当ListVIew数据发生改变的时候,调用Adapter的notifyDataSetChanged()--->DataSetObservable (notifyChanged())--->DataSetObserver(onChanged()),总之,最终调用了所有观察者的onChanged()方法,在onChanged()方法中又会调用ListVIew的 requestLayout()函数,重新布局ListView。

其中Observable

public abstract class Observable {
    /**
     * The list of observers.  An observer can be in the list at most
     * once and will never be null.
     */
    protected final ArrayList mObservers = new ArrayList();

    /**
     * Adds an observer to the list. The observer cannot be null and it must not already
     * be registered.
     * @param observer the observer to register
     * @throws IllegalArgumentException the observer is null
     * @throws IllegalStateException the observer is already 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.
     * @param observer the observer to unregister
     * @throws IllegalArgumentException the observer is null
     * @throws IllegalStateException the observer is not yet 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);
        }
    }

    。。。
}

DataSetObserver

public abstract class DataSetObserver {
    /**
     * This method is called when the entire data set has changed,
     * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
     */
    public void onChanged() {
        // Do nothing
    }

    /**
     * This method is called when the entire data becomes invalid,
     * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
     * {@link Cursor}.
     */
    public void onInvalidated() {
        // Do nothing
    }
}
3、例子

使用方式和ListView一样

public class CommonHorizontalListView extends HorizontalScrollView {

    private ListAdapter mAdapter;
    private AdapterDataObserver mObserver;
    private LinearLayout mContainer;
    private Context mContext;

    private int paddingLeft;
    private int paddingRight;
    private int paddingTopBottom;
    private int leftItemMargin;
    private int rightItemMargin;
    private float density;

    public CommonHorizontalListView(Context context) {
        this(context, null);
    }

    public CommonHorizontalListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        density = context.getResources().getDisplayMetrics().density;
        init();
    }

    private void init() {
        paddingLeft = (int) (10 * density);
        paddingRight = (int) (10 * density);
        paddingTopBottom = (int) (3 * density);
        leftItemMargin= (int) (5 * density);
        rightItemMargin= (int) (5 * density);
        mContainer = new LinearLayout(mContext);
        mContainer.setOrientation(LinearLayout.HORIZONTAL);
        LayoutParams p = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mContainer.setPadding(paddingLeft, paddingTopBottom, paddingRight, paddingTopBottom);
        addView(mContainer, p);
        setBackgroundColor(Color.WHITE);
        setSmoothScrollingEnabled(true);
        setHorizontalScrollBarEnabled(false);
    }

    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mObserver != null) {
            mAdapter.unregisterDataSetObserver(mObserver);
        }
        if (adapter != null) {
            this.mAdapter = adapter;
            mObserver = new AdapterDataObserver();
            mAdapter.registerDataSetObserver(mObserver);
            addViews();
        }
    }

    private void addViews() {
        mContainer.removeAllViews();
        int itemCount = mAdapter.getCount();
        for (int i = 0; i < itemCount; i++) {
            View child = mAdapter.getView(i, null, null);
            LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.leftMargin=leftItemMargin;
            params.rightMargin=rightItemMargin;
            mContainer.addView(child,params);
        }
    }

    private class AdapterDataObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            addViews();
            //requestLayout();
        }

        @Override
        public void onInvalidated() {
            //addViews();
            //requestLayout();
        }
    }

}
观察者模式_第2张图片
观察者模式 练习

注意:requeLayout() : 控件会重新执行 onMesure() 、onLayout() ,重新测量和定位。

3、分析EventBus

EventBus,事件总线,它使用发布订阅模式支持组件之间的通信,不需要显式地注册回调,比观察者模式更灵活,可用于替换Java中传统的事件监听器。

1、自己实现简单的事件总线

定义一个订阅者列表(Map,ArrayList< EventSubscription >>),其中EventSubscription存储了target、事件接收器方法和参数;在注册时,以事件接收器对象作为map的key;publish时,调用EventSubscription的onChanged()方法,通知每个订阅者发生了改变。并在这个方法中通过反射调用事件接收器方法(onEventMainThread)。

method.invoke(target,args);

为了灵活的定义方法接收器,可以使用注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscriber {
}

使用方法

EventBus.getInstance().subscribe(MainActivity.this);
EventBus.getInstance().unsubscribe(this);
EventBus.getInstance().publish("update from SecondFragment");


@Subscriber
public void onEventMainThread(String str){
    Toast.makeText(this,"MainActivity --- "+ str,Toast.LENGTH_LONG).show();
}
观察者模式_第3张图片
MY——EventBus
public class EventBus {

    //订阅者列表
    private Map,ArrayList> subscriptionByEvent=new HashMap<>();

    private EventBus(){
    }

    public static EventBus getInstance(){
        return Singleton.INSTANCE;
    }

    static class Singleton {
        static EventBus INSTANCE=new EventBus();
    }

    // register(target) 方法用于注册事件接收器的目标对象,我们需要保存这个对象,
    // 同时还需要查找这个对象中存在的事件接收器方法
    public void subscribe(Object target){
        Class clazz=target.getClass();
        Method[] methods=clazz.getMethods();
        for (Method method:methods){
            Subscriber subscriber=method.getAnnotation(Subscriber.class);
            if (subscriber!=null){
                Class[] parameters=method.getParameterTypes();
                if (parameters!=null && parameters.length==1){
                    ArrayList subscriptions;
                    if (subscriptionByEvent.containsKey(parameters[0])){
                        subscriptions = subscriptionByEvent.get(parameters[0]);
                    }else {
                        subscriptions =new ArrayList<>();
                    }
                    EventSubscription subscription =new EventSubscription(target,method,parameters[0]);
                    subscriptions.add(subscription);
                    subscriptionByEvent.put(parameters[0], subscriptions);
                }
            }
//            if (method.getName().equals("onEventMainThread")){
//                Class[] parameters=method.getParameterTypes();
//                ArrayList subscriptions;
//                if (subscriptionByEvent.containsKey(parameters[0])){
//                    subscriptions = subscriptionByEvent.get(parameters[0]);
//                }else {
//                    subscriptions =new ArrayList<>();
//                }
//                EventSubscription subscription =new EventSubscription(target,method,parameters[0]);
//                subscriptions.add(subscription);
//                subscriptionByEvent.put(parameters[0], subscriptions);
//            }
        }
    }

    //取消注册目标对象
    public void unsubscribe(Object target){
        Class clazz=target.getClass();
        Method[] methods=clazz.getMethods();
        for (Method method:methods){
            Subscriber subscriber=method.getAnnotation(Subscriber.class);
            if (subscriber!=null){
                Class[] parameters=method.getParameterTypes();
                if (parameters!=null && parameters.length==1){
                    if (subscriptionByEvent.containsKey(parameters[0])){
                        subscriptionByEvent.remove(parameters[0]);
                    }
                }
            }
//            if (method.getName().equals("onEventMainThread")){
//                Class[] parameters=method.getParameterTypes();
//                ArrayList eventObservers;
//                if (subscriptionByEvent.containsKey(parameters[0])){
//                    subscriptionByEvent.remove(parameters[0]);
//                }
//            }
        }
    }

    // 发送事件给 目标对象和事件接收器(onEventMainThread)方法。
    public void publish(Object event){
        Class clazz=event.getClass();
        if (subscriptionByEvent.containsKey(clazz)){
            ArrayList subscriptions =subscriptionByEvent.get(clazz);
            for (EventSubscription eventObserver : subscriptions){
                eventObserver.onChanged(event);
            }
        }
    }

}
观察者模式_第4张图片
MainActivity的Toast,有SecondFragmnent触发
2、EventBus解析

参考:
Android源码设计模式、教你自己实现一个事件总线EventBus、跟我一起写EventBus(一)、 Android事件总线(一)EventBus3.0用法全解析、

你可能感兴趣的:(观察者模式)