Android设计模式学习之观察者模式

2022-06-22 10:46:20 浏览数 (1)

观察者模式在实际项目中使用的也是非常频繁的,它最常用的地方是GUI系统、订阅——发布系统等。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者模式此时就派上用场了。

概述

观察者模式又被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

模式中的角色

抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

1.Subject 和 Observer 是一个一对多的关系,也就是说观察者只要实现 Observer 接口并把自己注册到 Subject 中就能够接收到消息事件; 2.Java API有内置的观察者模式类:java.util.Observable 类和 java.util.Observer 接口,这分别对应着 Subject 和 Observer 的角色; 3.使用 Java API 的观察者模式类,需要注意的是被观察者在调用 notifyObservers() 函数通知观察者之前一定要调用 setChanged() 函数,要不然观察者无法接到通知; 4.使用 Java API 的缺点也很明显,由于 Observable 是一个类,java 只允许单继承的缺点就导致你如果同时想要获取另一个父类的属性时,你只能选择适配器模式或者是内部类的方式,而且由于 setChanged() 函数为 protected 属性,所以你除非继承 Observable 类,否则你根本无法使用该类的属性,这也违背了设计模式的原则:多用组合,少用继承。

观察者模式示例

例如:MyPerson是被观察者

代码语言:javascript复制
public class MyPerson extends Observable {

    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers();
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        setChanged();
        notifyObservers();
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
        setChanged();
        notifyObservers();
    }

    @Override
    public String toString() {
        return "MyPerson [name="   name   ", age="   age   ", sex="   sex   "]";
    }
}

setChanged();告知数据改变,通过notifyObservers();发送信号通知观察者。

MyObserver是观察者

代码语言:javascript复制
public class MyObserver implements Observer {

    private int id;
    private MyPerson myPerson;

    public MyObserver(int id) {
        System.out.println("我是观察者---->"   id);
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public MyPerson getMyPerson() {
        return myPerson;
    }

    public void setMyPerson(MyPerson myPerson) {
        this.myPerson = myPerson;
    }

    @Override
    public void update(Observable observable, Object data) {
        System.out.println("观察者---->"   id   "得到更新");
        this.myPerson = (MyPerson) observable;
        System.out.println(((MyPerson) observable).toString());
    }

}

观察者接受到通知后,调用update方法进行更新操作。

代码语言:javascript复制
public class MainActivity extends Activity {

    private Button btnAddObserver;
    private Button btnChangeData;
    private MyPerson observable;
    private MyObserver myObserver;
    private List<MyObserver> myObservers;
    private ListView listview;

    private int i;

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            MyListAdapter myListAdapter = new MyListAdapter(MainActivity.this,
                    myObservers);
            listview.setAdapter(myListAdapter);

        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnAddObserver = (Button) findViewById(R.id.btn_add_observer);
        btnChangeData = (Button) findViewById(R.id.btn_change_data);
        listview = getListView();

        observable = new MyPerson();
        myObservers = new ArrayList<MyObserver>();

        btnAddObserver.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                myObserver = new MyObserver(i);
                i  ;
                observable.addObserver(myObserver);
                myObservers.add(myObserver);
                handler.sendEmptyMessage(0);
            }
        });

        btnChangeData.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                observable.setName("a"   i);
                observable.setAge(10   i);
                observable.setSex("男"   i);
                handler.sendEmptyMessage(0);
            }
        });

    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        observable.deleteObserver(myObserver);
    }
}

Android源码中的模式实现

在以前,我们最常用到的控件就是ListView了,而ListView最重要的一个点就是Adapter,在我们往ListView添加数据后,我们都会调用一个方法: notifyDataSetChanged(), 这个方法就是用到了我们所说的观察者模式。

跟进这个方法notifyDataSetChanged方法,这个方法定义在BaseAdapter中,代码如下:

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

可以发现,当数据发生变化时候,notifyDataSetChanged中会调用mDataSetObservable.notifyChanged()方法

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

}

mDataSetObservable.notifyChanged()中遍历所有观察者,并且调用它们的onChanged方法。

那么这些观察者是从哪里来的呢?首先ListView通过setAdapter方法来设置Adapter

代码语言:javascript复制
 @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中,代码如下 :

代码语言:javascript复制
 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, 代码如下 :

代码语言:javascript复制
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;
        }
    }

当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的onChanged方法。这就是一个观察者模式!

总结:AdapterView中有一个内部类AdapterDataSetObserver,在ListView设置Adapter时会构建一个AdapterDataSetObserver,并且注册到Adapter中,这个就是一个观察者。而Adapter中包含一个数据集可观察者DataSetObservable,在数据数量发生变更时开发者手动调用AdapternotifyDataSetChanged,而notifyDataSetChanged实际上会调用DataSetObservable的notifyChanged函数,该函数会遍历所有观察者的onChanged函数。在AdapterDataSetObserver的onChanged函数中会获取Adapter中数据集的新数量,然后调用ListView的requestLayout()方法重新进行布局,更新用户界面。

比较知名的使用观察者模式的开源框架有 EventBus AndroidEventBus otto

模式总结

优点

观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。

缺点

依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。

适用场景

当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。

一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

0 人点赞