您的位置:首页 > 移动开发 > Android开发

Android框架设计模式(三)——Observer Method

2016-03-23 21:03 579 查看
一观察者模式
什么是控制反转和依赖倒置
依赖倒置

控制反转

什么是观察者模式
定义

UML图

适用场景

二观察者模式在Android框架中的应用
BaseAdapter适配器

其他应用

一、观察者模式

在介绍观察者模式之前,先补充两个概念:IOC(控制反转)、DIP(依赖倒置)。

什么是控制反转和依赖倒置?

依赖倒置(控制反转),是框架设计的核心,因为有了它们会产生框架,框架的核心就是把【不变】的留在框架层次,把【变化】的留在应用层次,然后两个层次之间通过接口来实现沟通,降低耦合。它们两者本质是同样的,只是一个是从原则上面描述,一个是从方式上面描述。

依赖倒置:

高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。

抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。

控制反转:

高层框架不应该依赖于底层的实现,底层的实现应该依赖于高层的框
架,高层框架封装不变的部分,把变化的部分留出接口让底层实现,通
过接口来实现高层与底层之间的沟通。在这个沟通的过程中,框架是主
动的(即调用者),而底层的具体应用实现是被动的(被调用者)。


总体来说,依赖倒置与控制反转都是一个意思:依赖于接口编程,将具体的对象之间的关系通过轻量型的接口来分离,框架层通过接口调用应用层的不同实现(此处即框架与应用层解耦),达到反向控制的目的。依赖倒置是原则,控制反转是体现。

一般来说,IOC有两种实现的方式:

(1)继承( Inheritance)+卡隼函数(hook)——在 Template Pattern中使用

(2)委托( Delegation)+卡隼函数(hook)——在Observer模式中使用

什么是观察者模式?

定义:

百度百科:观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件(通知者)管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

通俗举例:

通俗来说,举古代打仗为例,敌军为数据集合(被观察者),军官为视图(观察者)。那么中间的通知者就是侦察兵,侦察兵在前线侦查敌情的变动,一有风吹草动就会通知军官,然后军官采取相应的方案。这里军官就是观察者,侦察兵就是通知者(不同角度可能角色会转换)。

UML图



适用场景

当一个对象的改变需要通知其它对象的时候,同时他又不知道具体有多
需要相互依赖的实体对象解耦,让他们共同依赖于抽象接口(类别),
这样即使两个具体对象有改动(只要接口没有变),也不会影响到对
方。而且观察者模式可以实现一个通知者,通知多个完全不同的观察
者。


二、观察者模式在Android框架中的应用

BaseAdapter适配器

Adapter(适配器),单独本身就是一种模式。但是在Android中,它还有另一个身份————观察者模式中的通知者。


Android里面实现观察者模式是基于组合,而不是继承的。即,观察者和通知者都是镶嵌在ListView和Adapter中,这样的好处是降低了宿主ListView、Adapter与Observer和Observerable的耦合性。

Android里面ListView的观察者模式的开始是下面经典代码:

MyAdapter adapter = new MyAdapter();
listView.setAdapter(adapter);


我们来看看setAdapter中做了什么事:

@Override
public void setAdapter(ListAdapter adapter) {
//解除之前的Observer
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//清除之前的缓存,重新更新视图集合
resetList();
mRecycler.clear();

//获取通知者引用(Adapter)
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();

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源码,里面有两个地方是观察者模式使用的核心:

即:

//获取通知者引用(Adapter)
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mDataSetObserver = new AdapterDataSetObserver();
//调用通知者的方法注册观察者
mAdapter.registerDataSetObserver(mDataSetObserver);


ListView获取Adapter的通知者引用,然后通过Adapter.registerDataSetObserver(),让通知者Adapter获得ListView的观察者接口,这样就实现了双向沟通的通道,绑定完成。

我们再来看通知者BaseAdapter的源码

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();//通知者引用

public boolean hasStableIds() {
return false;
}
//注册观察者绑定
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
//解除观察者绑定
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}

/**
* 通知已注册的观察者,数据集已经改变。任何观察者在收到该信息
* 之后必须更新自己。
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}

/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}

public boolean areAllItemsEnabled() {
return true;
}

public boolean isEnabled(int position) {
return true;
}

public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}

public int getItemViewType(int position) {
return 0;
}

public int getViewTypeCount() {
return 1;
}

public boolean isEmpty() {
return getCount() == 0;
}
}


从上面的源码我们可以看到,BaseAdapter里面有一个Observerable通知者对象。在ListView调用了setAdapter()方法后,就实现了通知者与观察者的绑定,当Adapter里面的数据集合改变时,容器Activity通过调用Adapter.notifyDataSetChanged()然后再通过Observerable.notifyChanged方法通知ListView数据集改变了,然后就会调用ListView中DataObserver的onChaned()方法更新界面。

我们来看看真正的Observer和Observerable接口的源码。

ListView中的AdapterDataSetObserver源码:

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


Adapter中的DataSetObserverable源码:

public class DataSetObservable extends Observable<DataSetObserver> {
/**
* 通知观察者数据集改变了
*/
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}

public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}


由于Android框架系统比较复杂,因此它并没有采取Gof的实现接口的方式来实现观察者模式,而是通过委托的方式(嵌套一个通知者、观察者),委托通知者和观察者来实现通知和接收通知的任务。

其他应用

观察者模式在Android中的应用还有很多。

比如:

广播机制中的BroadCast(通知者)和BroadReceiver(观察者);

Service中的Service(通知者)和ServiceConnection(观察者),当服务启动成功后,就调用onBind()方法通知ServiceConnection服务已经启动,通过 onServiceConnected()将Binder返回给观察者;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: