浅学使用Android的mvp模式+otto消息总线

对于mvp模式,越来越受到android开发者的关注,前段时间通过朋友知道的他们公司产品使用了mvp+otto模式重构项目,反馈效果不错,借此也自己去结合下,感觉有利有弊,具体的选择,可能要根据产品的应用场景和技术人员的模式定向吧!


先看下效果图吧:
这里写图片描述
项目结构目录
这里写图片描述
otto:图中的BusManage
通过定一个获取bus对象,建立单例模式,节约内存消耗,也方便之后调用。

public class BusManager extends Bus {

    private static class InterInstance{
        private static final BusManager instance = new BusManager();
    }

    private BusManager(){
    }

    public static BusManager getInstance(){
        return InterInstance.instance;
    }
}

以上通过创建静态内部类来实现单例,具体好处是,这种方式并未加锁,因为第一次加载BusManager类时,并不会实例化单例对象,只有第一次调用getInstance()方法时会导致虚拟机加载InterInstance类,这种
方式不仅能保证对象的单一性,还避免加锁带来的性能问题,又启动了延迟加载的优化。
MainBean:实体类

public class MainBean {

    private String name;
    private String phone;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

}

MainBean用于封装数据的实体类
View:图中IMainView和MainActivity
View相对应于Activity,我们通过创建一个View的接口跟presenter进行交互,降低耦合,View通过实现这个接口去绘制视图,与用户交互。

public interface IMainView {

    void showLoading();

    void hideLoading();

    void showError();

    void showResult(MainBean bean);
}

视图接口提供四个接口方法:显示加载视图、隐藏加载视图、显示加载失败视图、显示成功数据

public class MainActivity extends AppCompatActivity implements IMainView {


        private RelativeLayout rlLoading;
        private RelativeLayout rlError;
        private android.widget.TextView tvResult;
        MainPresenter mainPresenter;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            this.tvResult = (TextView) findViewById(R.id.tvResult);
            this.rlError = (RelativeLayout) findViewById(R.id.rlError);
            this.rlLoading = (RelativeLayout) findViewById(R.id.rlLoading);
            mainPresenter = new MainPresenter(this);
            mainPresenter.loadData();
            rlError.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mainPresenter.clickError();
                }
            });
        }

        @Override
        public void showLoading() {
            rlLoading.setVisibility(View.VISIBLE);
            rlError.setVisibility(View.INVISIBLE);
        }

    @Override
    public void hideLoading() {
        rlLoading.setVisibility(View.INVISIBLE);
    }

    @Override
    public void showError() {
        hideLoading();
        rlError.setVisibility(View.VISIBLE);
    }

    @Override
    public void showResult(MainBean bean) {
        tvResult.setText(bean.getName()+bean.getPhone());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mainPresenter.unRegister();
    }
}

通过实现View接口,去完成用户交互需求。通过创建Presenter对象去处理view和model之间关系。
Model:图中IMainModel和MainModel
我们也需要创建一个抽象接口(IMainModel)来降低耦合,也利于单元测试,model通过实现接口来处理数据。

public interface IMainModel {

    void loadMainData();

    void clickError();
}

model的接口。提供加载数据和重新点击加载失败页面处理方法

public class MainModel implements IMainModel {

    @Override
    @Produce
    public synchronized void loadMainData() {//模拟加载数据

        new AsyncTask<String,String,String>(){

            @Override
            protected String doInBackground(String... params) {
                try {
                    Thread.sleep(3000);


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                MainBean bean = new MainBean();
                bean.setName("WX_JIN");
                bean.setPhone("交流群:313870489");
                if ((Math.random()*2) >1.5){//模拟加载数据成功或者失败
                    BusManager.getInstance().post("获取数据失败");
                }else{
                    BusManager.getInstance().post(bean);
                }
            }
        }.execute();


    }

    @Override
    public void clickError() {
        loadMainData();
    }
}

model实现类,处理加载数据。这边就是通过otto消息总线去分发加载数据后处理结果。
Presenter:图中MainPresenter
中间枢纽Presenter,作为model和view之间的交互。

public class MainPresenter {

    private IMainView mainView;
    private IMainModel mainModel;

    public MainPresenter(IMainView mainView){
        this.mainView = mainView;
        this.mainModel = new MainModel();
        BusManager.getInstance().register(this);
    }

    //加载数据
    public void loadData(){
        mainView.showLoading();
        mainModel.loadMainData();
    }

    public void clickError(){
        mainView.showLoading();
        mainModel.clickError();
    }

    public void unRegister(){
        BusManager.getInstance().unregister(this);
    }

    @Subscribe //成功返回数据
    public void getSuccessData(MainBean bean){
        mainView.hideLoading();
        mainView.showResult(bean);
    }

    @Subscribe //失败返回
    public void getError(String error){
        mainView.showError();
    }
}

Presenter通过去实例化model接口和获取实例化的view接口去处理两者之前的数据。Presenter这边使用otto获取到Model处理完数据之后,再通过View的抽象接口更新View显示的信息。这样就实现了完整的解耦UI与逻辑操作。

如果UI有涉及ListView的Adapter,这样就要在View和Presenter中多出一个Adapter,通过这个Adapter来操作Model和View,Adapter需要实现View接口,这要就保证与Presenter之间的关联不变,扩展性也比较强,也有更好的灵活性。

以上就是自己尝试用mvp和otto一起使用,总结和记录下自己过程中的经验,其中肯定有许多不足,希望日后慢慢补充和修复。


项目源码下载路径


私家博客:http://soubw.com/
CSDN:http://blog.csdn.net/wx_jin

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作者codyer,源码ElegantBus,ElegantBus 是一款 Android 平台,基于 LivaData 的消息总线框架,这是一款非常 优雅 的消息总线框架。如果对 ElegantBus 的实现过程,以及考虑点感兴趣的可以看看前几节自吹如果只是想先使用的,可以跳过,直接到跳到使用说明和常见 LivaData 实现的 EventBus 比较消息总线使用反射入侵系统包名进程内 Sticky跨进程 Sticky跨 APP Sticky事件可配置化线程分发消息分组跨 App 安全考虑常驻事件 StickyLiveEventBus:white_check_mark::white_check_mark::white_check_mark::x::x::x::x::x::x::x:ElegantBus:x::x::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark::white_check_mark:来龙去脉自吹ElegantBus 支持跨进程,且支持跨应用的多进程,甚至是支持跨进程间的粘性事件,支持事件管理,支持事件分组,支持自定义事件,支持同名事件等。之所以称之为最优雅的总线,是因为她不仅实现了该有的功能,而且尽量选用最合适,最轻量,最安全的方式去实现所有的细节。 更值得夸赞的是使用方式的优雅!前言随着 LifeCycle 的越来越成熟,基于 LifeCycle 的 LiveData 也随之兴起,业内基于 LiveData 实现的 EventBus 也如雨后春笋一般拔地而起。出于对技术的追求,看过了无数大牛们的实现,各位大神们思路也是出奇的神通,最基础的 LiveData 版 EventBus 其实大同小异,一个单例类管理所有的事件 LivaData 集合。如果不清楚的可以随便网上找找反正基本功能 LivaData 都支持了,实现 EventBus 只需要把所有事件管理起来就完事了。业内基于 LiveData 实现的 EventBus,其实考虑的无非就是下面提到的五个挑战,有的人考虑的少,有的人考虑的多,于是各种方案都有。ElegantBus 主要是集合各家之优势,进行全方面的考虑而产生的。五个挑战 之 路途险阻挑战一 : 粘性事件背景 LivaData 的设计之初是为了数据的获取,因此无论是观察开始之前产生的数据,还是观察开始之后产生的数据,都是用户需要的数据,只要是有数据,当 LifeCycle 处于激活状态,数据就会传递给观察者。这个我们称之为 粘性数据。 这种设计对于事件来说有时候就不那么友好了,之前的事件用户可能并不关心,只希望收到注册之后发生的事件。挑战二 : 多线程发送事件可能丢失背景 同样是因为使用场景的原因,LivaData 设计在跨线程时,使用 post 提交数据,只会保留最后一次数据提交的值,因为作为数据来说,用户只需要关心现在有的数据是什么。挑战三 : 跨进程事件总线背景 有时候我们应用需要设置多进程,不同模块可能允许在不同进程中,因为单例模式每个进程都有一份实体,所有无法达到跨进程,这时候设计 IP 方案选择。说明 这里提一下为什么不选用广播方式,对广播有一定了解的都知道,全局广播会有信息泄露,信息干扰等问题,而且开销也比较大,因此全局广播并不适合这种情况。 也许有人会说可以用本地广播,然而,本地广播目前来说并不是很好的选择。Google 官方也在 LocalBroadcastManager 的说明里面建议使用 LiveData 替代: 原文地址原文如下:2018 年 12 月 17 日版本 1.1.0-alpha01 中将弃用 androidx.localbroadcastmanager。原因LocalBroadcastManager 是应用级事件总线,在您的应用中使用了层违规行为;任何组件都可以监听来自其他任何组件的事件。 它继承了系统 BroadcastManager 不必要的用例限制;开发者必须使用 Intent,即使对象只存在且始终存在于一个进程中。由于同一原因,它未遵循功能级 BroadcastManager。 这些问题同时出现,会对开发者造成困扰。替换您可以将 LocalBroadcastManager 替换为可观察模式的其他实现。合适的选项可能是 LiveData 或被动流,具体取决于您的用例。更明显的原因是,本地广播好像并不支持跨进程~挑战四 : 跨应用(权限问题以及粘性问题)背景 跨进程相对来说还比较好实现,但是有的时候用户会有跨应用的需求,其实这个也是 IPC 范畴,为什么单独提出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值