Android MVP 架构

作者: 菜鸟考官 | 来源:发表于2018-06-11 17:38 被阅读55次

    本Demo使用 Okhttp3、Retrofit2、Rxjava2 ,使用AutoDispose解决RxJava内存泄漏
    Github:
    https://github.com/RookieExaminer/MvpDemo

    什么是MVP,为什么要用MVP?
    网上介绍MVP的很多,百度一下你就知道,至于为什么要用MVP,当然是由于它的优势了:
    1.代码简洁
    此处的简洁是逻辑的简洁,而不是代码本身 举个栗子

    image.png

    比如购物车界面,有很多请求网络的地方:获取购物车商品列表、购物车商品的删除、购物车商品的购买等等, 这么多网络请求,如果都写在一个Activity,而且还有大量逻辑判断,那这个Activity的行数~ 看着就让人头痛, 即便写了注释,维护起来也是比较麻烦的
    2.降低耦合,方便维护
    MVP的使用,使Activity中的网络请求剥离出来 成为model、presenter,model只负责网络的请求、pesenter负责处理请求网络后的数据处理:加载中 成功 or 失败 取消加载;最后View进行界面的展示


    image.png image.png

    Start 看图:

    image.png

    嗯哼? 不是 Model、Presenter、View这三个 么,怎么又多出来个Contract,这又是什么鬼?
    这就涉及到MVP的缺点了,正所谓,金无足赤,人无完人,MVP既然有优点当然也有它的缺点了
    MVP在实现代码简洁的同时,额外增加了大量的接口、类,不方便进行管理,于是Contract就登场了。

    Contract 百度翻译 : 合同;契约;协议
    Contract 如其名,是一个契约,将Model、View、Presenter 进行约束管理,方便后期类的查找、维护。

    下面演示下登陆的MVP实现方式:
    (示例代码由开发项目中剥离到Demo中,登陆接口使用的是玩安卓的登陆API:http://www.wanandroid.com/blog/show/2

    首先,创建一个登陆的Contract:
    
    public interface MainContract {
        interface Model { }
    
        interface View extends BaseView { }
    
        interface Presenter { }
    }
    其次创建Presenter、Model、View 对应Contract中的接口;
    public class MainPresenter implements MainContract.Model{}
    public class MainModel implements MainContract.Presenter{}
    public class MainActivity  implements MainContract.View {}
    
    

    完整的Contract:

    public interface MainContract {
        interface Model {
            Flowable<BaseObjectBean<LoginBean>> login(String username, String password);
        }
    
        interface View extends BaseView {
            @Override
            void showLoading();
    
            @Override
            void hideLoading();
    
            @Override
            void onError(Throwable throwable);
    
            void onSuccess(BaseObjectBean<LoginBean> bean);
        }
    
        interface Presenter {
            /**
             * 登陆
             *
             * @param username
             * @param password
             */
            void login(String username, String password);
        }
    }
    

    在MainContract 中
    Model接口 创建对应的联网请求的方法,将Presenter提交的字段放到联网请求中,发送给服务器
    View 接口 创建在界面上显示加载中、取消加载以及登陆成功、失败的方法
    Presenter 接口 创建 登陆的方法,以及需要提交的字段 (username、password)

    MainModel的完整代码:

    public class MainModel  implements MainContract.Model {
        @Override
        public Flowable<BaseObjectBean<LoginBean>> login(String username, String password) {
            return RetrofitClient.getInstance().getApi().login(username,password);
        }
    }
    

    Model类实现MainContract.Model 接口中的 login(String username, String password)方法,将username、password放在联网请求中,进行请求服务器。

    MainView 的完整代码:

    public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.View {
    
        @BindView(R.id.et_username_login)
        TextInputEditText etUsernameLogin;
        @BindView(R.id.et_password_login)
        TextInputEditText etPasswordLogin;
    
        @Override
        public int getLayoutId() {
            return R.layout.activity_main;
        }
    
        @Override
        public void initView() {
            mPresenter = new MainPresenter();
            mPresenter.attachView(this);
        }
    
        /**
         * @return 帐号
         */
        private String getUsername() {
            return etUsernameLogin.getText().toString().trim();
        }
    
        /**
         * @return 密码
         */
        private String getPassword() {
            return etPasswordLogin.getText().toString().trim();
        }
    
        @Override
        public void onSuccess(BaseObjectBean bean) {
    
            Toast.makeText(this, bean.getErrorMsg(), Toast.LENGTH_SHORT).show();
    
        }
    
        @Override
        public void showLoading() {
            ProgressDialog.getInstance().show(this);
        }
    
        @Override
        public void hideLoading() {
            ProgressDialog.getInstance().dismiss();
        }
    
        @Override
        public void onError(Throwable throwable) {
    
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // TODO: add setContentView(...) invocation
            ButterKnife.bind(this);
        }
    
        @OnClick(R.id.btn_signin_login)
        public void onViewClicked() {
            if (getUsername().isEmpty() || getPassword().isEmpty()) {
                Toast.makeText(this, "帐号密码不能为空", Toast.LENGTH_SHORT).show();
                return;
            }
            mPresenter.login(getUsername(), getPassword());
        }
    }
    

    MainActivity 中实现 MainContract.View中的方法 ,在实现的方法中,进行进度条加载、和登陆成功or失败的UI的展示:

            @Override
            void showLoading();
    
            @Override
            void hideLoading();
    
            @Override
            void onError(Throwable throwable);
    
            void onSuccess(BaseObjectBean<LoginBean> bean);
    

    MainPresenter 的完整代码:

    public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {
    
        private MainContract.Model model;
    
        public MainPresenter() {
            model = new MainModel();
        }
    
        @Override
        public void login(String username, String password) {
            if (!isViewAttached()) {
                return;
            }
            mView.showLoading();
            model.login(username, password)
                    .compose(RxScheduler.<BaseObjectBean<LoginBean>>Flo_io_main())
                    .as(mView.<BaseObjectBean<LoginBean>>bindAutoDispose())
                    .subscribe(new Consumer<BaseObjectBean<LoginBean>>() {
                        @Override
                        public void accept(BaseObjectBean<LoginBean> bean) throws Exception {
                            mView.onSuccess(bean);
                            mView.hideLoading();
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            mView.onError(throwable);
                            mView.hideLoading();
                        }
                    });
        }
    }
    

    MainPresenter 实现MainContract.Presenter 接口中的 login(String username, String password) 方法

    实例化Model,在MainPresenter login(String username, String password)方法中,调用model的网络请求,将username、password放在model的login()方法中,进行请求服务器。
    请求服务器前 使用MainContract.View中的 mView.showLoading()方法,进行显示加载中;在成功失败的回调中,使用对应的方法,以及取消加载。

    其中BasePresenter、BaseView 是对Presenter以及View进行的封装

    BaseView类:

    public interface BaseView {
    
        /**
         * 显示加载中
         */
        void showLoading();
    
        /**
         * 隐藏加载
         */
        void hideLoading();
    
        /**
         * 数据获取失败
         * @param throwable
         */
        void onError(Throwable throwable);
    
        /**
         * 绑定Android生命周期 防止RxJava内存泄漏
         *
         * @param <T>
         * @return
         */
        <T> AutoDisposeConverter<T> bindAutoDispose();
    
    }
    

    至于为什么不把onSuccess()方法也封装,是因为请求网络,服务器返回的值是不一样的,在Contract > View接口中根据bean类设置onSuccess()

    BasePresenter类:

    public class BasePresenter<V extends BaseView> {
        protected V mView;
    
    
        /**
         * 绑定view,一般在初始化中调用该方法
         *
         * @param view view
         */
        public void attachView(V view) {
            this.mView = view;
        }
    
        /**
         * 解除绑定view,一般在onDestroy中调用
         */
    
        public void detachView() {
            this.mView = null;
        }
    
        /**
         * View是否绑定
         *
         * @return
         */
        public boolean isViewAttached() {
            return mView != null;
        }
    
    
    }
    

    时间有限,暂时就先这样,具体可下载Demo查看 ↓

    本Demo: https://github.com/RookieExaminer/MvpDemo
    MVP快速生成类的插件: https://github.com/githubwing/MVPHelper

    参考:
    Android MVP架构搭建:
    http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html?1508484926
    Android架构中添加AutoDispose解决RxJava内存泄漏:
    https://www.jianshu.com/p/8490d9383ba5

    相关文章

      网友评论

      • 83c3cbb5a86a:你好,我所使用的FragmentActivity或AppCompatActivity没有实现LifecycleOwner,必须手动实现后bindAutoDispose才不会报错,但看到你的代码是没有实现LifecycleOwner,请问这是什么原因导致的呢?(Activity引用的包都一样)
        83c3cbb5a86a:@菜鸟考官 解决了,是support版本导致的,我用的是26,demo里面的是27,不是所有版本的v7包的fragmentactivity都有实现LifecycleOwner。谢谢啦
        菜鸟考官:@菜鸟考官 你可以下载demo看看
        菜鸟考官:在BaseMvpActivity(BaseMvpFragment)中实现 bindAutoDispose(),activity(Fragment) 继承,Presenter中使用bindAutoDispose()

      本文标题:Android MVP 架构

      本文链接:https://www.haomeiwen.com/subject/ebpysftx.html