目录
1. 介绍
1.1 定义
1.2 作用
1.3 优缺点
2. 实践案例Demo
2.1 利用Java内置的Observer/Observable
2.1.1 实现方式
2.2 自定义观察者/被观察者
2.2.1 具体实现方式
3. Android中包含观察者模式的组件源代码解析
3.1 ListView中观察者模式解析
4. 观察者模式与发布者/订阅者模式的区别
从字面意思上可以看出,观察者模式,那肯定有观察对象、被观察对象以及观察这个动作,观察对象通过观察动作来获取被观察对象状态是否有所改变。比如,小明和小王都订阅了陈老师的博客,当陈老师更新博客时,小明小王就会收到陈老师更新博客的通知,小明和小王就是观察者对象,陈老师的博客就是被观察者对象,观察动作则是博客系统里的设置。看起来这很像发布与订阅,所以很多人都说观察者模式也叫发布者/订阅者模式,但这是错的,观察者模式与发布/订阅模式是不一样的,具体看第四章4. 观察者模式与发布者/订阅者模式的区别。
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式主要用来解耦,降低被观察者和观察者之间的耦合度,当被观察者对象的状态发生改变时,会通知观察者对象发生改变,但不知道是哪些观察者对象以及多少观察者对象发生改变,以此来降低耦合度。
优点:
1. 观察者对象与被观察者对象是抽象耦合的。
2.建立一套触发机制(也就是观察动作)。
缺点:
1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间.
2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
在安卓中实现观察者模式有两种,一、利用Java内置的Observer/Observable来实现,二、自定义观察者被观察者。
利用java.util.Observable和java.util.Observer来创建被观察者对象以及观察者,用notifyObservers()来通知观察者,因为Java替我们做了很多事情,所以用起来十分的简单。
Demo: 观察姓名爱好有没有变化,如果有变化,则显示出来(3秒延时后显示出来),效果如下?(点击图片放大查看):
1. 继承Observable来创建我们的被观察者对象
public class UserData extends Observable {
private String mName;
private String mHobby;
public UserData() {
getNewDataFromRemote();
}
private void getNewDataFromRemote(){
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
setUserData("Jere", "playing basketball !");
}
}, 3000);
}
public void setUserData(String name, String hobby) {
this.mName = name;
this.mHobby = hobby;
setChanged();
notifyObservers();
}
public String getName() {
return mName;
}
public String getAge() {
return mHobby;
}
}
用了Handler设置3秒的延时来模拟数据的变化,从Null到被赋值,数据发生变化,然后通知所有Observer观察者(Demo中只有一个观察者)。
注意: 在notifyObservers()方法前面要设置一下setChanged()方法,不然观察者收不到数据变化通知。原因:setChanged()方法就是代表被观察者对象的数据改变了,只有当被观察者数据改变了才会去通知观察者们,否则观察者们将不会收到状态改变的通知,也就不会做出任何反应。
2. 实现Observer接口来创建观察者
public class FunctionOneActivity extends AppCompatActivity implements Observer {
private Observable mUserDataRepositoryObservable;
private TextView mNameTv;
private TextView mHobbyTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
mNameTv = findViewById(R.id.name_tv);
mHobbyTv = findViewById(R.id.hobby_tv);
mUserDataRepositoryObservable = new UserData();
mUserDataRepositoryObservable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof UserData) {
UserData userDataRepository = (UserData) o;
mNameTv.setText(userDataRepository.getName());
mHobbyTv.setText(String.valueOf(userDataRepository.getAge()));
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mUserDataRepositoryObservable.deleteObserver(this);
}
}
当然我们也可以自定义我们的被观察者及观察者对象。
Demo:模拟NBA的文字直播方式,观察湖人与凯尔特人的比赛比分,效果如下?
1. 创建Observable(被观察者)的主题接口
public interface GameScoreSubject {
void addObserver(GameScoreObserver gameScoreObserver);
void removeObserver(GameScoreObserver gameScoreObserver);
void notifyAllObserver();
}
2. 具体实现Observable
public class GameScoreData implements GameScoreSubject{
private String mLakerTeamScore;
private String mBostonTeamScore;
private List mGameObserverList;
public GameScoreData() {
mGameObserverList = new ArrayList<>();
}
public String getLakerTeamScore() {
return mLakerTeamScore;
}
public void setLakerTeamScore(String lakerTeamScore) {
this.mLakerTeamScore = lakerTeamScore;
}
public String getBostonTeamScore() {
return mBostonTeamScore;
}
public void setBostonTeamScore(String bostonTeamScore) {
this.mBostonTeamScore = bostonTeamScore;
}
public void setTwoTeamScores() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
setLakerTeamScore("102");
setBostonTeamScore("99");
notifyAllObserver();
}
}, 3000);
}
@Override
public void addObserver(GameScoreObserver gameScoreObserver) {
if (!mGameObserverList.contains(gameScoreObserver)) {
mGameObserverList.add(gameScoreObserver);
}
}
@Override
public void removeObserver(GameScoreObserver gameScoreObserver) {
if (mGameObserverList.contains(gameScoreObserver)) {
mGameObserverList.remove(gameScoreObserver);
}
}
@Override
public void notifyAllObserver() {
for (GameScoreObserver gameScoreObserver: mGameObserverList) {
gameScoreObserver.scoreChanged(mLakerTeamScore, mBostonTeamScore);
}
}
}
3.创建Observer(观察者)接口
public interface GameScoreObserver {
void scoreChanged(String lakerTeamScores, String bostonTeamScores);
}
4.具体实现Observer
public class FunctionTwoActivity extends AppCompatActivity implements GameScoreObserver{
private TextView mLakerTeamScoreTv;
private TextView mBostonTeamScoreTv;
private TextView mEspnReportTv;
private GameScoreData mGameScoreData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mLakerTeamScoreTv = findViewById(R.id.laker_score_tv);
mBostonTeamScoreTv = findViewById(R.id.boston_score_tv);
mEspnReportTv = findViewById(R.id.espn_media_tv);
mGameScoreData = new GameScoreData();
mGameScoreData.addObserver(this);
ESPNMediaScoreObserver espnMediaScoreObserver = new ESPNMediaScoreObserver();
mGameScoreData.addObserver(espnMediaScoreObserver);
mGameScoreData.setTwoTeamScores();
}
@Override
protected void onDestroy() {
super.onDestroy();
mGameScoreData.removeObserver(this);
}
@Override
public void scoreChanged(String lakerTeamScores, String bostonTeamScores) {
mLakerTeamScoreTv.setText(lakerTeamScores);
mBostonTeamScoreTv.setText(bostonTeamScores);
}
public class ESPNMediaScoreObserver implements GameScoreObserver {
@Override
public void scoreChanged(String lakerTeamScores, String bostonTeamScores) {
String newestReportString = "newest scores: \nLaker: " + lakerTeamScores
+ " vs " + "Boston: " + bostonTeamScores + " \nKobe three ball killed game!!\nOMG!";
mEspnReportTv.setText(newestReportString);
}
}
}
Android中有很多组件都用观察者模式来设计的,比如ListView、ExpandListView、LiveData等等。
当我们改变了ListView的数据,然后调用notifyDataSetChanged()来通知刷新UI页面。
1. Adapter 定义定义观察者接口
public interface Adapter {
//注册一个观察者方法
void registerDataSetObserver(DataSetObserver observer);
//注销一个观察者方法
void unregisterDataSetObserver(DataSetObserver observer);
}
2. 具体实现被观察者
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);
}
//通知所有观察者
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
}
3. 定义观察者的抽象类
public abstract class DataSetObserver {
//数据改变时触发
public void onChanged() {
// Do nothing
}
//数据无效时触发
public void onInvalidated() {
// Do nothing
}
}
4. 具体实现观察者
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
//获取Adapter中的数据数量
mItemCount = getAdapter().getCount();
// 检测先前无效的游标已被重新填充新数据的情况。
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
//刷新UI页面
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
//记住主机活动正在停止并稍后重新启动的情况的当前状态
mInstanceState = AdapterView.this.onSaveInstanceState();
}
//数据无效,所以我们应该重置状态
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
//刷新UI页面
requestLayout();
}
}
从上图中可以很直观的发现,发布者/订阅者模式与观察者最大的区别就在与,发布者和订阅者之间还存在一个消息队列组件,用来做发布者与订阅者之间的信息传递,所以发布者与订阅者彼此都不知道对方的存在,完成解偶。
区别总结:
1. 发布者/订阅者彼此之间不需要知道,通过消息队列来进行通信。
2. 相对比观察者模式,发布者与订阅者之间完全解耦。
3. 当被观察者状态发生改变时就会通知所有观察者,观察者收到消息,并做出改变,这是同步进行的。但发布者与订阅者是通过中间组件信息队列来通信的,所以是异步的。
4. 观察者模式像是在同一个应用程序中实现,但发布者与订阅者模式更像是跨应用程序中应用。