Android设计模式--观察者模式

时间是一只藏在黑暗中温柔的手,在你一出神一恍惚之间,物走星移

一,定义

观察者模式是定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新

字面意思很好理解,我们常用的订阅-发布系统就是观察者模式。观察者模式是一个使用率非常高的模式,因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。

二,使用场景

1,关联行为场景,需要注意的是,关联行为是可拆分的,而不是组合关系

2,事件多级触发场景

3,跨系统的消息交互场景,如消息队列,事件总线的处理机制

观察者模式可以分为四个角色:

1,抽象主题,也就是被观察的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

2,具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫具体被观察者角色。

3,抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。

4,具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。

三,使用案例

在我们的开发中,都会遇到下载升级的场景,需要边下载边显示升级进度,还需要在下载完成或者下载失败之后更新状态为正在安装或者下载失败,可能有的应用需要显示进度的页面还不止一个,这个时候使用观察者模式就再好不过了。

首先,我们需要定义一个观察者抽象接口,实现两个方法:

java 复制代码
/*
 *  @创建者:   袁震
 *  @创建时间:  2023/7/25 10:37
 *  @描述:    观察者接口
 */

public interface DownLoadObserver {

    //下载状态发生变化
    public void onDownloadStateChanged(boolean isSuccess,String path);

    //下载进度发生变化
    public void onDownloadProgressChanged(int progress);


}

然后实现一个下载管理器,这个下载管理器,实际上就是被观察者,里面注册了观察者,实现了下载功能,并在下载进度和状态改变时,通知观察者:

java 复制代码
/*
 *  @创建者:   袁震
 *  @创建时间:  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);
                        }
                    });
        });
    }

    
}

在实际使用中:

java 复制代码
/*
 *  @创建者:   袁震
 *  @创建时间:  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源码设计模式第二版

相关推荐
zzhongcy29 分钟前
复合索引 (item1, item2, item3 ) > (?, ?, ?) 不起作用,EXPLAIN 后type=ALL(全表扫描)
android·数据库
冬奇Lab1 小时前
稳定性性能系列之十三——CPU与I/O性能优化:Simpleperf与存储优化实战
android·性能优化
像风一样自由2 小时前
android native 中的函数动态注册方式总结
android·java·服务器·安卓逆向分析·native函数动态注册·.so文件分析
nono牛2 小时前
Makefile中打印变量
android
魅影骑士00102 小时前
柯里化函数
后端·设计模式
没有了遇见3 小时前
Android 关于RecycleView和ViewPager2去除边缘反馈
android
城东米粉儿3 小时前
android gzip数据压缩 笔记
android
城东米粉儿3 小时前
android 流量优化笔记
android
似霰4 小时前
HIDL Hal 开发笔记10----添加硬件访问服务(Java 层调用 HIDL)
android·framework·hal
佛系打工仔5 小时前
绘制K线第三章:拖拽功能实现
android·前端·ios