在阅读本片文章之前希望你先看下上篇的博客Java设计模式之观察者模式
##1、分析理解
Android中我们常用的观察者是:DataSetObserver、ContentObserver
ContentObserver : 可能我们在学ContentProvider时接触过ContentObserver;当我们添加、删除、更新数据时,通过它来为我们共享的数据添加监听器。
DataSetObserver:它我们乍一看很陌生,其实它离我们很近,我们几乎经常用到它;只不过我们看不到而已,你记得AbsListView吧!每当我们AbsListView.setAdapter()时,就会自动为我们在Adapter上注册个DataSetObserver监听器。所以我们通过BaseAdapter.notifyDataSetChanged()时,都会更新我们的AbsListView。
由于ContentObserver比较复杂,但与DataSetObserver的原理是相同的;本篇博客我们只分析DataSetObserver。
通过上篇的博客我们已经知道,Java的观察者模式无非就是涉及到四个角色:
1、抽象主题(Subject)
2、具体主题(ConcreteSubject)
3、抽象观察者(Observer)
4、具体观察者(ContreteObserver)
当我们的具体主题(ConcreteSubject)中更新数据时,需要所有的具体观察者(ConcreteSubject)实时更新数据。在上篇博客中Java设计模式之观察者模式有一个控制类,它就是ObserverDemo(拥有main()方法),它的任务就是负责将具体主题与具体观察者建立联系(其实就是MVC中的Controller)。但它的还不是十分的明显,在Android中这个Controller的作用将会体现的淋漓尽致。下面让我们一起去揭开它的面纱!!!但它到底是谁呢?
##2、Android中观察者模式的四个角色
下面让从源码中分析下Android中观察者模式的四个角色:
###1、抽象主题(Subject)
/**
* Provides methods for (un)registering arbitrary observers in an ArrayList.
*/
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* 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);
}
}
/**
* Remove all registered observer
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
###2、具体主题(ConcreteSubject)
/**
* A specialization of Observable for DataSetObserver that provides methods for
* invoking the various callback methods of DataSetObserver.
*/
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) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
/**
* Invokes onInvalidated on each observer. Called when the data set being monitored
* has changed such that it is no longer valid.
*/
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
###3、抽象观察者(Observer)
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
}
}
###4、具体观察者(ContreteObserver)
在Android中具体的观察者有多种,下面我介绍一个我们常用的一个具体观察者。
像ListView、GridView中在设置适配器时,内部都会自动为我们添加一个具体的观察者。
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
}
##3、Android观察者模式的Controller
在上面我们我们引出了一个Controller,它的具体作用是什么呢?其实就是MVC中的Controller。
1、把我们的具体观察者与具体主题联系(如何注册/反注册监听器)在一起。
2、当我们的主题(Subject)数据发生改变时,我们只需要操作Controller,然后由Controller去执行如何更新每一个观察者。
这个Controller到底是谁呢?
它就是我们经常用到的适配器BaseAdapter,当我们ListView中的数据需要更新时,我们会调用BaseAdapter.notifyDataSetChanged();在BaseAdapter中封装了一个我们的主题,我们会通过具体主题告诉所有的监听器。
具体主题调用的方法:
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
这时候你可能会想,我从创建ListView到自定义BaseAdapter,就没有为ListView设置过任何监听器。到底这个监听器是如何被注册到具体主题上的呢?这时候我们就需要看看ListView的源代码了。
ListView的源代码比较多,我们只看我们需要的即可!!!
/**
* Sets the data behind this ListView.
*
* The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
* depending on the ListView features currently in use. For instance, adding
* headers and/or footers will cause the adapter to be wrapped.
*
* @param adapter The ListAdapter which is responsible for maintaining the
* data backing this list and for producing a view to represent an
* item in that data set.
*
* @see #getAdapter()
*/
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
//答案就在这里,ListView内部为自己注册了一个监听器AdapterDataSetObserver
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();
}
学习到这里,我们知道了ListView在setAdapter()时,并为自己注册了一个监听器。所以当我们通过Adapter.notifyDataSetChange()时会更新我们的界面数据。
到这里我们就结束了Android中DataSetObserver监听器的分析。
##4、额外篇(ContentObserver)
Android中的另一个监听器ContentObserver与DataSetObserver的原理时一样的。在这里我就简单的说一下它的作用的。
1、当我们通过ContentProvider把数据共享时,有时候当数据发生改变时,我们需要及时的得到通知,这时ContentObserver就排上用场了。
2、通过1的作用我再引申一下。在Android系统中的应用程序如短信、通讯录等都在数据改变时为我们注册了一个监听器,所以我们完全可以通过Uri与数据建立关联,当我们通过ContentObserver得到数据更新时,及时重新更新我们的数据。
短信的Uri共有以下几种:
content://sms/inbox 收件箱
content://sms/sent 已发送
content://sms/draft 草稿
content://sms/outbox 发件箱 (正在发送的信息)
content://sms/failed 发送失败
content://sms/queued 待发送列表 (比如开启飞行模式后,该短信就在待发送列表里)
通过这几个Uri,我们可以实现通短信的监听!!!