欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android MVP架构详解

程序员文章站 2022-03-09 16:16:07
...

为什么要重视程序的架构设计

对程序进行架构设计的原因,归根结底是为了 提高生产力 。通过设计是程序模块化,做到模块内部的 高聚合 和模块之间的 低耦合 (如依赖注入就是低耦合的集中体现)。

这样做的好处是使得程序开发过程中,开发人员主需要专注于一点,提高程序开发的效率,并且更容易进行后续的测试以及定位问题。

但是,设计不能违背目的,对于不同量级的工程,具体的架构实现方式必然不同,不要为了设计而设计,为了架构而架构。比如一个android app如果只有几个Java文件,那只需要做点模块和层次的划分就可以了。引入框架或者架构增加了工作量,降低了生产力。

所以在开发的时候需要考虑:

1)当前这个项目是否需要以最快速度上线。比如有些创业公司,争取的就是时间,公司老板是要拿着这个app是去找投资的。

2)如果这个项目开发周期还可以,第一个版本就可以把app架构做好。因为一个App肯定是朝着慢慢做大的方向去的,如果等业务到了一定程度了,再去重构的话,成本就有点大。

什么是MVP?

MVP架构由MVC发展而来。在MVP中,M代表Model,V代表View,P代表Presenter。

Model 负责获取数据,数据的来源可以是网络或本地数据库等;

View 负责界面数据的展示,与用户进行交互;

Presenter 是Model与View之间的通信的桥梁,将Model与View分离开来。

MVP架构图:

Android MVP架构详解

所以MVP的架构有如下好处:

1)降低了View和Model的耦合,通过Presenter层来通信;

2)把视图层抽象到View接口,逻辑层抽象到Presenter接口,提高了代码的可读性、可维护性;

3)Activity和Fragment功能变得更加单一,只需要处理View相关的逻辑;

4)Presenter抽象成接口,就可以有多种实现,方便单元测试。

 

Android MVP架构详解

image.png

Start 看图:

Android MVP架构详解

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.Presenter{} 
public class MainModel implements MainContract.Model{}
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



作者:菜鸟考官
链接:https://www.jianshu.com/p/ae0b21d3238a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关标签: android学习