手记

Android MVP 架构设计实现 深度解耦

随着 UI 创建技术的功能日益增强,UI 层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让 View 专注于处理数据的可视化以及与用户的交互,同时让 Model 只关系数据的处理,基于 MVC(Model View Controller) 模式的 MVP(Model-View-Presenter) 模式应运而生。目前MVP模式在 Android 应用开发中越来越重要了,大家也都在讨论 MVP 的理论,体系化的资料非常少,所以诞生出了本篇文章。MVP 模式是 MVC 模式的一个演化版本,MVP 模式能有效降低 View 的复杂度,避免业务逻辑被塞进 View 中,MVP 模式会解除 View 与 Model 的耦合,同时又带来了良好的可扩展性、可测试性。

MVP 模式可以分离显示层和逻辑层,它们之间通过接口进行通信,降低耦合。理想化的 MVP 模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖于具体,而是依赖于抽象,这使得 Presenter 可以运用于任何实现了 View 逻辑接口的 UI,使之具有更广泛的适用性,保证了灵活度。

1.MVP模式的三种角色

角色说明
Model主要做一些数据处理, 网路请求。Presenter 需要通过 Model 层存取、获取数据,Model是封装了数据库 Dao 层或者网络获取数据的角色,或者两种数据获取方式的集合。
Presenter交互中间人,核心逻辑,处理 View 的业务逻辑,沟通 View 和 Model 的桥梁,Presenter 持有的 View、Model 引用都是抽象,它从 Model 层检索数据后返回给 View 层,使得 View 和 Model 没有耦合,也将业务逻辑从 View 层抽取出来,经常会执行耗时操作。
View用户界面,Activity、Fragment 或者某个 View 控件,含有一个 Presenter 成员变量,通常 View 层需要实现一个逻辑接口,将 View 上的操作通过会转交给 Presenter 进行实现,最后 Presenter 调用 View 逻辑接口将结果返回给 View 元素。


很多缺乏经验的工程师很可能会将各种各样的业务逻辑塞进某个 Activity、Fragment 或者自定义控件中,使得这些组件的单个类型臃肿不堪。MVP 模式可以让 UI 界面和数据分离,职责单一,易于维护。MVP 模式也并不是一个标准化的模式,它有很多实现方式,我们也可以根据自己的需求和自己认为对的方式去修正 MVP 的实现方式,它可以随着 Presenter 的复杂程度变化。只要保证我们是通过 Presenter 将 View 和 Model 解耦合、降低类型复杂度、各个模块可以独立测试、独立变化,这就是正确的方向。

2.实际项目中MVP模式的实现

MVP 模式的实现我们以最简单的用户登录为例进行说明。MVP 模式一个很大特点就是定义接口比较多,代码量变大,下面来看一下实际项目中MVP模式的实现。

首先定义 MVP 基本的三个接口,基本上是固定写法:

public interface IModel {}public interface IPresenter<V extends IView> {
    /**
     * 绑定
     * @param view
     */
    void attachView(V view);    /**
     * 防止内存的泄漏, 清除Presenter与Activity之间的绑定
     */
    void detachView();    /**
     * @return 获取View
     */
    V getIView();
}public interface IView {}

然后定义契约类,定义 Presenter、View 用到的一些接口方法:

public class LoginContract {
    public interface LoginView {
        String getUserName();
        String getPwd();        void loginSuccess(LoginBean loginBean); // 登录成功,展示数据
        void loginFail(String failMsg);
    }    public interface LoginPresenter {
        void login(String name, String pwd); // 业务逻辑
    }
}

定义 LoginModel 类,主要做一些数据处理, 网路请求:

public class LoginModel extends BaseModel {
    private boolean isLogin = false;    public boolean login(@NonNull String username, @NonNull String pwd, @NonNull final DataListener
            listener) {        // 此处推荐使用 RxJava + Retrofit 进行网络请求
        // 网络请求成功 isLogin = true; listener.successInfo(articles);
        // 网络请求失败 isLogin = false; listener.failInfo(str);
        return isLogin;
    }    // 通过接口产生信息回调
    public interface DataListener<T> {
        void successInfo(T result);        void failInfo(String result);
    }
}

LoginModel 的父类:

public class BaseModel implements IModel {
    // 做一些数据处理, 网路请求的初始化操作}123123

然后定义交互中间人 LoginPresenter,处理 View 的业务逻辑,它是沟通 View 和 Model 的桥梁,Presenter 持有的 View、Model 引用都是抽象,且经常会执行耗时操作:

public class LoginPresenter extends BasePresenter<LoginActivity> implements
        LoginContract.LoginPresenter {
    @Override
    public void login(String name, String pwd) {        if (!getIView().checkNull()) {
            ((LoginModel) getiModelMap().get("login")).login(name, pwd, new LoginModel
                    .DataListener<LoginBean>() {                @Override
                public void successInfo(LoginBean result) {
                    getIView().loginSuccess(result); // 成功
                }                @Override
                public void failInfo(String result) {
                    getIView().loginFail(result); // 失败
                }
            });
        }
    }    @Override
    public HashMap<String, IModel> getiModelMap() {        return loadModelMap(new LoginModel());
    }    @Override
    public HashMap<String, IModel> loadModelMap(IModel... models) {
        HashMap<String, IModel> map = new HashMap<>();
        map.put("login", models[0]);        return map;
    }
}

Presenter 如果持有 Activity 的强引用,在请求结束之前 Activity 被销毁了,那么由于网络请求还没有返回,导致 Presenter 一直持有 Activity 对象,使得 Activity 无法被回收,此时就容易发生内存泄漏,解决这个问题需要通过弱引用来解决,LoginPresenter 的父类 BasePresenter 如下:

public abstract class BasePresenter<V extends IView> implements IPresenter {
    private WeakReference<V> mViewRef; // View接口类型的弱引用
    /**
     * 建立关联
     * @param iview
     */
    @Override
    public void attachView(IView iview) {
        mViewRef = new WeakReference(iview);
    }    /**
     * 解除关联
     */
    @Override
    public void detachView() {        if (mViewRef != null) {
            mViewRef.clear();
            mViewRef = null;
        }
    }    /**
     * 获取View
     * @return
     */
    @Override
    public V getIView() {        return mViewRef.get();
    }    /**
     * 判断是否与View建立了关联
     * @return 建立则返回true
     */
    public boolean isViewAttached() {        return mViewRef != null && mViewRef.get() != null;
    }    public abstract HashMap<String, IModel> getiModelMap();    /**
     * @param models
     * @return
     * 添加多个model,如有需要
     */
    public abstract HashMap<String, IModel> loadModelMap(IModel... models);
}

到这里,Model 和 Presenter 已经都有了,还差 View。Activity 实现需要 IView 和 LoginContract.LoginView 接口,并需要建立与 Presenter 之间的联系,Activity 的业务逻辑都将交给 Presenter 进行处理,处理结果通过 LoginContract.LoginView 接口回调给 Activity 类:

public class LoginActivity extends AppCompatActivity implements IView, LoginContract.LoginView {
    LoginPresenter mPresenter;    // 代码省略
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);        if (mPresenter != null) {
            mPresenter.attachView(this);
        }        // 代码省略
        // 请求数据,当请求成功后,调用 LoginContract.LoginView 的 loginSuccess 方法将数据传递给 View
        mPresenter.login(getUserName(), getPwd()); 
    }    // 代码省略
    @Override
    public void loginSuccess(LoginBean loginBean) {        // 更新UI
    }    @Override
    public void loginFail(String failMsg) {
    }    /**
     * 注意MVP与Activity、Fragment生命周期的处理
     */
    @Override
    protected void onDestroy() {        super.onDestroy();        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}

此时 MVP 的关系此时已经建立成功了。Activity 此时的作用只是做一些 View 的初始化工作,职责单一、功能简单、易于维护。此外,Presenter 与 Model 也依赖于抽象而不是具体,使得 Model 的具体实现可以被轻易地替换。Presenter 与 View 的低耦合使得系统能够应对 UI 的易变性问题,也使得系统的 View 模块变得易于维护。最后 Activity 在实际开发中也会向上抽取出 BaseActivity,比较简单,这里不再给出。

原文链接:http://www.apkbus.com/blog-822721-68180.html

0人推荐
随时随地看视频
慕课网APP