首页 > 代码库 > 【安卓笔记】数据适配器(adapter)中的观察者模式

【安卓笔记】数据适配器(adapter)中的观察者模式

ListView要想显示数据,需要用到数据适配器即Adapter。而当我们删除ListView的某个条目时,数据适配器中的数据源必然发生改变,这时候我们通过调用适配器类提供的notifyDataSetChanged方法通知listview数据发生改变,请求重新绘制。
这其中其实使用了一种比较常见的设计模式,即观察者模式。

在分析数据适配器中涉及到的观察者模式之前,我们先简单了解下什么是观察者模式。
观察者模式的定义:定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
技术分享
上面是观察者模式的类图。Subject类中通过Attach/Detach方法去绑定/解绑一个或者多个观察者(Observer)对象,当Subject出现某种Observer感兴趣的事件时,Subject将会调用notify方法通知所有绑定的Observer对象,调用其update方法更新数据。

下面我们试着分析数据适配器中的观察者模式。
这里我们可以从BaseAdapter的notifyDataSetChanged开始跟踪源码。
定位到该方法,我们发现只有一行代码,即调用了mDataSetObservable对象的notifyChanged方法。
 public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
这个mDataSetObservable即被观察的对象,它是一个DataSetObservable类型。我们来看下其实现:
package android.database;
public class DataSetObservable extends Observable<DataSetObserver> {
 
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
}
可以看到,在它的notifyChanged方法中调用了每一个观察者的onChanged回调方法,这个mObservers即观察者集合,它的定义在DataSetObservable的父类Observable中,另外,这里通过泛型指定了观察者类型必须为DataSetObserver类型。
我们打开Observable源码:
package android.database;
import java.util.ArrayList;
public abstract class Observable<T> {

    protected final ArrayList<T> mObservers = new ArrayList<T>();
    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);
        }
    }

    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);
        }
    }
    ...
}
这个类定义了一个ArrayList类型的观察者集合,并且提供了两个方法用来注册/解除注册 一个观察者,其实就是调用集合的remove/add方法。
到这里我们明白了调用适配器的notifyDataSetChanged方法最终会通知所有已经注册过的观察者们,调用每个观察者的onChanged方法。
如果ListView在删除一个条目后,想要更新界面,必然在此之前注册了一个观察者,并且该观察者的onChanged方法中必然会有界面重绘的代码。而ListView跟适配器打交道的方式是setAdapter方法,可想而知,此方法中肯定有注册观察者的代码。
根据这个思路,我们定位到ListView的setAdapter方法:
 public void setAdapter(ListAdapter adapter) {
       ... ...
        super.setAdapter(adapter);
        if (mAdapter != null) {
           ... ...
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);
         ... ...
        } else {
          ... ...
        }
        requestLayout();
    }
果然,在这个方法中我们找到了注册观察者的代码,但是这里的观察者是AdapterDataSetObserver类型的,而Adapter要求的是DataSetObserver类型的,那么很显然,AdapterDataSetObserver是DataSetObserver的子类。该类的定义在ListView的父类AbsListView中:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }
   ... ...
    }
而AbsListView中的AdapterDataSetObserver又继承了AdapterView类中的AdapterDataSetObserver。最终,我们再AdapterView中找到AdapterDataSetObserver:
 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();
        }
  ... ...
    }
可以看到,这个类的确继承了DataSetObserver,并且在onChanged中调用了requestLayout去刷新布局。到这里我们明白了整个流程,另外也看到了观察者模式在实际项目中的使用,确实很强大。
最后附上一张图,方便大家理解。
技术分享










【安卓笔记】数据适配器(adapter)中的观察者模式