十一、观察者设计模式

1. 观察者模式的介绍

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

例如:我们在使用应用市场下载应用时,我们的通知栏会有下载进度显示,我们的详情页会有进度显示,我们的列表中也会有下载进度显示,这就是一个典型的观察者设计模式,多个观察者监听同一个下载进度。

2. 观察者模式的使用场景

  • 事件的多级触发场景。
  • 跨系统的消息交换场景。

3. 观察者模式的UML类图

观察者设计模式UML类图

UML角色介绍

  • Subject: 抽象主题角色,也就是被观察的角色,抽象主题角色的所有观察者对象的引用保存在一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

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

  • Obser: 为所有具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。

  • ConcreteObserver: 具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。

4. 观察者模式的简单实现

(1)、 首先定义一个观察者接口:

public interface Observer {

            /**
             * 更新接口
             *
             * @param subject 传入主题对象,获取主题的状态信息
             */
            public void update(Subject subject);
}

观察者接口里面只有一个方法update(Subject suject),当有更新时,将被观察者对象传进来,然后获取其状态信息。

(2)、接着定义具体的观察者,实现观察者接口:

public class ConcreteObserver implements Observer {
            private ConcreteSubject concreteSubject;
            private String name;

            public ConcreteObserver(String name) {
                this.name = name;
            }

            @Override
            public void update(Subject subject) {
                concreteSubject = (ConcreteSubject) subject;
                String state = concreteSubject.getState();
                System.out.println(name + " 观察到: " + state);
            }
}

具体观察者在收到更新后,在update方法里面做出具体的操作

(3)、然后,定义具体的被观察对象

public abstract class Subject {

        //用来保存注册的观察者
        private List<Observer> observers = new ArrayList<Observer>();

        /**
         * 注册观察者,将其加入到集合中
         *
         * @param observer
         */
        public void registerObserver(Observer observer) {
            if (!observers.contains(observer)) {
                observers.add(observer);
            }
        }

        /**
         * 取消注册
         *
         * @param observer
         */
        public void unrigisterObserver(Observer observer) {
            if (observers.contains(observer)) {
                observers.remove(observer);
            }
        }


        /**
         * 通知所有的观察者
         */
        public void notifyObservers() {
            for (Observer observer : observers) {
                observer.update(this);
            }
        }
}

被观察者里有一个集合,用户存储所有的观察者对象的引用。

使用registerObserver方法添加到集合中,使用unregisterObserver方法将观察者对象从集合中移除。

最后调用notifyObservers方法遍历所有的观察者,调用它们的update方法,将被观察者自身对象传递进去。

具体的每个观察者在自己的update方法里面做出相应的行为。

(4)、具体的被观察者

public class ConcreteSubject extends Subject {
        private String state;

        public String getState() {
            return state;
        }

        public void change(String newSate) {
            this.state = newSate;
            this.notifyObservers();
        }
}

这个具体的观察者,其实在日常开发中,根本没有抽象观察者,都只有一个具体的被观察者,这里为了实现UML类图里面的对应关系,所以多了一个抽象的观察者。

5. 观察者模式在Android源码中

我们平时在更新ListView时,都会使用notifyDataSetChanged()方法来更新界面,其实这就是一个观察者设计模式。

我们先简单来说一下大致步骤。

(1)、首先我们会通过setAdapter来设置Adapter,在Adapter里面设置了一个观察者。观察者里面有一个changed()方法,onChanged()方法面会重新刷新布局。

(2)、当我们的数据发生变化时,会调用Adapter里面的notifyDataSetChanged()方法来更新数据,在notifyDataSetChanged()方法里面,最终会遍历所有的的观察者,并调用其changed()方法。

下面我们从源码的角色:

(1)、首先我们我们先找出观察者,从刚刚的分析中,我们知道setAdapter会设置一个观察者。所以我们从setAdapter入手。

public void setAdapter(ListAdapter adapter) {
        //忽略以上代码
        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            checkFocus();

            //这段代码就是观察者。
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);

            mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

            int position;
            if (mStackFromBottom) {
                position = lookForSelectablePosition(mItemCount - 1, false);
            } else {
                position = lookForSelectablePosition(0, true);
            }
            setSelectedPositionInt(position);
            setNextSelectedPositionInt(position);

            if (mItemCount == 0) {
                // Nothing selected
                checkSelectionChanged();
            }
        } else {
            mAreAllItemsSelectable = true;
            checkFocus();
            // Nothing selected
            checkSelectionChanged();
        }

        requestLayout();
}

从以上代码我们可以看到,setAdapter里面我们看到如下代码:

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

从名字我们不难猜测到这是一个观察者。我们继续点进去,在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();
            }
        }
}

我们发现AdapterDataSetObserver继承自AdapterView.AdapterDataSetObserver,继续跟进:

class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;

        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            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();
            requestLayout();
        }

        @Override
        public void onInvalidated() {
            mDataChanged = true;

            if (AdapterView.this.getAdapter().hasStableIds()) {
                // Remember the current state for the case where our hosting activity is being
                // stopped and later restarted
                mInstanceState = AdapterView.this.onSaveInstanceState();
            }

            // Data is invalid so we should reset our state
            mOldItemCount = mItemCount;
            mItemCount = 0;
            mSelectedPosition = INVALID_POSITION;
            mSelectedRowId = INVALID_ROW_ID;
            mNextSelectedPosition = INVALID_POSITION;
            mNextSelectedRowId = INVALID_ROW_ID;
            mNeedSync = false;

            checkFocus();
            requestLayout();
}

上述代码就是我们想要的观察者的庐山真面目。里面我们注意发现有onChanged()方法,这个就相当于UML类图的update()方法。

(2)、 接着我们找出被观察对象。

我们从BaseAdapter里面的notifyDataSetChanged()方法入手,我们看到如下代码:

public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
}

同时我们可以看到如下代码:

public void registerDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.registerObserver(observer);
}

public void unregisterDataSetObserver(DataSetObserver observer) {
            mDataSetObservable.unregisterObserver(observer);
}

从方法的名字可以看出,以上两个方法的作用是,注册观察者和反注册观察者。

我们继续跟进,看到如下代码:

public class DataSetObservable extends Observable<DataSetObserver> {

    public void notifyChanged() {
        synchronized(mObservers) {
            //遍历所有的观察者,调用每个观察者的onChanged方法。
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
}

从上面的代码中我们可以看到,notifyChanged()方法遍历了所有的观察者,并调用了其onChanged()方法。

分析到这里,我们可以肯定的说,这就是一个典型的观察者设计模式。

最后画张图来总结一下,让调用更加清晰明了。

Adapter中的观察者设计模式

6. 观察者模式的Android开发

下面模拟我们在应用市场下载通过一个应用时,可以同时在通知栏,详情页面,ListView同时观察到同步的进度。模拟每隔一秒钟将下载进度发给所有的观察者。

(1)、首先定义一个观察者接口接口:Listener

public interface Listener {
        //更新进度的方法
        void update(int progress);
}

(2)、具体的观察者:DownLoadListener

public class DownListener implements Listener {

        //观察者的名称
        public String name;

        public DownListener(String name) {
            this.name = name;
        }

        //具体观察者在收到更新后所进行的操作
        @Override
        public void update(int progress) {
            System.out.println(name + " ,下载进度: " + progress);

        }
}

(3)、下载服务DownloadService。模拟下载服务主要用到了计时器工具类,Timer和TimerTask,每隔一秒钟,通过handler将进度发送出去。handler收到进度后,遍历所有的观察者,调用所有监听的观察者的update方法,将进度传递进去。

public class DownloadService {
        private List<Listener> listeners = new ArrayList<Listener>();
        private static final int DOWN = 1;
        private int progress = 1;

        private Timer mTimer;
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case DOWN:
                        //通知所有的观察者
                        notifyAllListeners(msg.arg1);
                        break;
                    default:
                        break;
                }
            }
        };

        public DownloadService() {
            mTimer = new Timer();
        }

        //模拟开启服务
        public void startService() {
            setTimerTask();
        }


        //定时服务
        private void setTimerTask() {
            mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.arg1 = ++progress;
                    message.what = DOWN;
                    handler.sendMessage(message);
                }
            }, 1000, 1000/* 表示1000毫秒之後,每隔1000毫秒執行一次 */);
        }


        //遍历通知所有的观察者
        public void notifyAllListeners(int progress) {
            for (Listener listener :
                    listeners) {
                listener.update(progress);
            }
        }

        //注册监听
        public void registerService(Listener listener) {
            if (!listeners.contains(listener)) {
                listeners.add(listener);
            }
        }

        //取消监听
        public void ungisterService(Listener listener) {
            if (listeners.contains(listener)) {
                listeners.remove(listener);
            }
        }
}

(4)、 测试代码:

public class MainActivity extends AppCompatActivity {

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //构造三个观察者
        Listener listener1 = new DownListener("观察者1");
        Listener listener2 = new DownListener("观察者2");
        Listener listener3 = new DownListener("观察者3");

        //下载服务
        DownloadService downloadService = new DownloadService();
        //注册监听者
        downloadService.registerService(listener1);
        downloadService.registerService(listener2);
        downloadService.registerService(listener3);
        //开启服务
        downloadService.startService();

    }
}

7. 总结

  • 优点:
    • 观察者与被观察者之间属于轻度的关联关系,两个之间是抽象呢耦合的。易于扩展。
  • 缺点:
    • 由于观察者模式是一条触发链,当观察者比较多的时候,靠能会导致执行效率下降,一个观察者的卡顿,会导致整整体的执行效率。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值