【设计模式】Android之观察者模式

1,观察者模式(发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)

1)

定义了对象之间的一对多依赖,这样一来当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

2)用途

通常被用来实现事件处理系统。
在实际应用中,如果一个事物改变了需要通知另一个事物此时就需要使用观察者来监控

3)实现

①Java内置的观察者模式。

是一个类。不灵活。

②自己实现

UML图
i>新建接口观察者。
ii>新建接口主题(被观察者)。
iii>观察者类实现观察者接口。(观察者可能有很多)
iv>被观察者实现主题接口。
v>实例化被观察者(单例) -> 实例观察者 -> 被观察者添加观察者 -> 数据变化后,被观察者调用notifyObserver()方法,通知所有观察者数据改变 -> 观察者接收到数据改变信息后处理。

4)注意

①不要依赖于观察者被通知的次序。这违反了松耦合原则。
②被观察者和观察者之间松耦合。一个被观察者(主题)可以有很多观察者。
③被观察者(主题)是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这样可以得到更干净的OO设计。
④针对具体实现编程,回导致我们以后在增加或删除布告板时,必须修改程序。观察者模式,将改变的地方分离并封装,符合OO设计原则。

5)优缺点

优:
实现观察者和被观察者之间的松耦合
支持一对多通知,只要是注册过的观察者都能收到通知
缺:
可能需要调用多次都能收到所需要的全部数据
也可能会有多余的数据

2,Android中的应用

观察者模式在android系统中的应用还是相当广泛的,如BroadcastReceiver、各种回调机制的实现(Activity的生命周期、按钮的点击事件、线程的run()方法)、Adapter 数据的更新。
比较知名的观察者模式的开源框架:EventBus、 AndroidEventBus、 otto(也是EventBus的一种实现)。
观察者模式看起来很高大上,其实说白了就是一个类维护了另一个类的一个集合,并通过这个集合绑定解绑或调用另一个类的方法,只不过,在设计底层框架时候,利用了多态的特性抽象出了接口和抽象类,以便适用于各种场合。
我们不能盲目的套用设计模式,因为多态会增加运行时开销,需权衡利弊,在使用。

1)Adapter数据的更新

① BaseAdapter.java(抽象的被观察者)

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

②MyAdapter(具体的被观察者)

ListView.java

public class ListView {
    ...代码省略
    @Override
    public void setAdapter(ListAdapter adapter) {
        ...代码省略
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        ...代码省略
    }
    ...代码省略
}

通过listView.setAdapter(myAdapter);将 AdapterDataSetObserver 注册到 Adapter 的DataSetObservable中。
在我们往ListView添加数据后,myAdapter.notifyDataSetChanged(),通知ListView更新数据。

③DataSetObservable(抽象观察者)

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes onChanged on each observer. Called when the data set being observed has
     * changed, and which when read contains the new state of the data.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 调用所有观察者的onChanged方式
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

}

当数据发生变化时候,notifyDataSetChanged中会调用mDataSetObservable.notifyChanged()方法,此时会遍历所有观察者,并且调用它们的onChanged方法。

④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();
        }

        // 代码省略

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

2)ContentObserver(内容观察者)

①概念

内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当 ContentObserver 所观察的Uri发生变化时,便会触发它。

②观察特定Uri的步骤如下

创建我们特定的 ContentObserver 派生类,必须重载父类构造方法,必须重载 onChange() 方法去处理回调后的功能实现。
利用 context.getContentResolover() 获得 ContentResolove 对象,接着调用 registerContentObserver() 方法去注册内容观察者。
由于 ContentObserver 的生命周期不同步于 Activity 和 Service 等,因此,在不需要时,需要手动的调用 unregisterContentObserver() 去取消注册。

3、demo

1)通过观察者监控网络的变换(根据连接的服务器IP的不同做不同的操作)

/**
 * 被观察者
 * @author luo
 */
public interface Watched_net {

    /**
     * 增加观察者
     * @param watcher_net
     */
    public void add(Watcher_net watcher_net);
    /**
     * 移除观察者权限
     * @param watcher_net
     */
    public void remove(Watcher_net watcher_net);
    /**
     * 可以实现行为并向观察者传输信息
     * @param s
     */
    public void notifyWatcher(String s);
}
public interface Watcher_net {

    /**
     * 用来获取更新信息接收的方法  
     * 观察到新的变化后,做什么动作
     * @param content
     */
    public void updateNotify(String s); 
}
/**
 * 被观察者 观察是否有网络切换
 * 
 * @author luo
 *
 */
public class Net_switch implements Watched_net {

    /** use to Package Watcher_net */
    private List<Watcher_net> list = new ArrayList<>();
    /** 网络切换标志,内网为"i",外网为"e"; */
    private String Net_flag = "";

    final private static Net_switch net_switch = new Net_switch();

    public static Net_switch getInstance() {
        return net_switch;
    }

    private Net_switch() {

    }

    public String getNet_flag() {
        return Net_flag;
    }

    public void setNet_flag(String net_flag) {
        Net_flag = net_flag;
    }

    @Override
    public void add(Watcher_net watcher_net) {
        list.add(watcher_net);
    }

    @Override
    public void remove(Watcher_net watcher_net) {
            list.remove(watcher_net);
    }

    /**
     * 通知所有的观察者有新的变化
     */
    @Override
    public void notifyWatcher(String s) {

            for (Watcher_net watcher : list) {
                watcher.updateNotify(s);
            }
    }
}
    private void setWatcher() {
            Net_switch.getInstance().remove(GFragment.getInstance());
            Net_switch.getInstance().remove(CFragment.getInstance());
            Net_switch.getInstance().add(T.getInstance());
    }

2)RecyclerView的item点击回调

3)MVP

猜你喜欢

转载自blog.csdn.net/sunshinetan/article/details/79667230