Android MVP 架构

作者:菜鸟考官
链接:https://www.jianshu.com/p/ae0b21d3238a

本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<MainPresenterimplements 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.Viewimplements 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/MvpDemoMVP快速生成类的插件: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

登入/注册
卧槽~你还有脸回来
没有账号? 忘记密码?