时间是一只藏在黑暗中温柔的手,在你一出神一恍惚之间,物走星移
一,定义
观察者模式是定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新
字面意思很好理解,我们常用的订阅-发布系统就是观察者模式。观察者模式是一个使用率非常高的模式,因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。
二,使用场景
1,关联行为场景,需要注意的是,关联行为是可拆分的,而不是组合关系
2,事件多级触发场景
3,跨系统的消息交互场景,如消息队列,事件总线的处理机制
观察者模式可以分为四个角色:
1,抽象主题,也就是被观察的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
2,具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫具体被观察者角色。
3,抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。
4,具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。
三,使用案例
在我们的开发中,都会遇到下载升级的场景,需要边下载边显示升级进度,还需要在下载完成或者下载失败之后更新状态为正在安装或者下载失败,可能有的应用需要显示进度的页面还不止一个,这个时候使用观察者模式就再好不过了。
首先,我们需要定义一个观察者抽象接口,实现两个方法:
/*
* @创建者: 袁震
* @创建时间: 2023/7/25 10:37
* @描述: 观察者接口
*/
public interface DownLoadObserver {
//下载状态发生变化
public void onDownloadStateChanged(boolean isSuccess,String path);
//下载进度发生变化
public void onDownloadProgressChanged(int progress);
}
然后实现一个下载管理器,这个下载管理器,实际上就是被观察者,里面注册了观察者,实现了下载功能,并在下载进度和状态改变时,通知观察者:
/*
* @创建者: 袁震
* @创建时间: 2023/7/25 10:34
* @描述: 下载管理器 被观察者
*/
public class DownloadManager {
private static final String TAG = "DownloadManager";
private int errorCount =0;//下载失败次数
// 私有的构造函数
private DownloadManager() {
}
// 私有的静态内部类
private static class Holder {
private static DownloadManager instance = new DownloadManager();
}
// 开放的获取单例对象的方法
public static DownloadManager getInstance() {
return DownloadManager.Holder.instance;
}
private ArrayList<DownLoadObserver> mObservers = new ArrayList<DownLoadObserver>();
//注册观察者
public void registerObserver(DownLoadObserver observer){
if(observer!= null && !mObservers.contains(observer)){
Log.d(TAG,"----添加observer");
mObservers.add(observer);
}
}
//注销观察者
public void unregisterObserver(DownLoadObserver observer){
if(observer!= null && mObservers.contains(observer)){
mObservers.remove(observer);
}
}
// 通知下载状态发生变化
public synchronized void notifyDownloadStateChanged(boolean isSuccess,String path) {
for (DownLoadObserver observer : mObservers) {
observer.onDownloadStateChanged(isSuccess,path);
}
}
// 通知下载进度发生变化
public synchronized void notifyDownloadProgressChanged(int progress) {
for (DownLoadObserver observer : mObservers) {
observer.onDownloadProgressChanged(progress);
}
}
public void downLoadApk(String fileUrl,String name){
//调用线程池中的线程
A10ThreadExecutor.getExecutorService(A10ThreadExecutor.HANDLE_MODULE).execute(() -> {
String rootDir = "/sdcard/Update_APK/";
Log.d(TAG, "下载路径为:" + rootDir);
Utils.delAllFile(rootDir);
OmniHttp.downLoad(fileUrl)
.savePath(rootDir)
.saveName(name)
.execute(new DownloadProgressCallBack<String>() {
@Override
public void update(long bytesRead, long contentLength, boolean done) {
notifyDownloadProgressChanged((int) ((bytesRead * 100)/contentLength));
}
@Override
public void onStart() {
}
@Override
public void onComplete(String path) {
notifyDownloadStateChanged(true,path);
Log.d(TAG, "-------下载成功path=" + path);
}
@Override
public void onError(final ApiException e) {
errorCount++;
errMethod(fileUrl,name,errorCount);
Log.e(TAG, "----下载失败:" + e.getMessage()+"--失败次数:"+errorCount);
}
});
});
}
}
在实际使用中:
/*
* @创建者: 袁震
* @创建时间: 2023/7/24 14:14
* @描述: 升级页面
*/
@RequirePresenter(UpdatePresenter.class)
public class UpdateActivity extends BaseActivity<UpdatePresenter> implements UpdateContract.IUpdateView, DownLoadObserver {
private static final String TAG = "UpdateActivity";
public static final String UPDATE_DATE = "UPDATE_DATE";
private ProgressBar pb;
private TextView txtProgress;
private TextView txtContent;
@Override
public int getLayoutId() {
return R.layout.activity_update;
}
@Override
public void initFields() {
pb = findViewById(R.id.progressBar);
txtProgress = findViewById(R.id.txt_progress);
txtContent = findViewById(R.id.txt_content);
//注册观察者
DownloadManager.getInstance().registerObserver(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String stringExtra = getIntent().getStringExtra(UPDATE_DATE);
bean = GsonUtils.json2Bean(stringExtra, UpdatePackageBean.class);
}
@Override
public void onDownloadStateChanged(boolean isSuccess,String path) {
Log.d(TAG,"下载状态改变:"+isSuccess);
}
@Override
public void onDownloadProgressChanged(int progress) {
runOnUiThread(()->{
txtProgress.setVisibility(View.VISIBLE);
txtContent.setText("正在下载新的安装程序,请不要关机或断电");
txtProgress.setText(progress+"%");
pb.setProgress(progress);
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销观察者
DownloadManager.getInstance().unregisterObserver(this);
}
}
这样就通过观察者模式实现了UI和业务逻辑的解耦已经实时更新问题。
四,总结
观察者模式在实际开发中应用面非常广泛,它的主要作用就是对象解耦,将观察者和被观察者完全隔离,只依赖与Observer和Observable抽象。
优点:
1,观察者和被观察者之间是抽象耦合,应对业务变化
2,增加系统灵活性,可扩展性。
缺点:
程序中包括一个被观察者,多个观察者,开发和调试等内容会比较复杂,在java中消息的通知默认是 顺序执行,一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般考虑采用 异步的方式。
参考文献:Android源码设计模式第二版