Android MVP升级路(一)乞丐版的自我救赎

目录

  • 引言
  • 为何用MVP架构
  • MVP理论知识
  • 乞丐版MVP架构模式的代码实现
  • MVP中的代码复用场景
  • 平民版MVP架构 - base层顶级父类
  • Fragment怎么办
  • 时尚版MVP架构 - Model层的单独优化

引言

记得第一次接触MVP开发是上大学的时候,当时看了数十篇关于MVP的文章,这里不得不吐槽一下国内技术帖子的质量真是参次不齐啊。看完以后一直懵懵懂懂的,总觉有几处关键的地方没搞清可是文章中却一带而过了,好比:android

  • 关于如何在Activity中高效的复用Presenter和View;
  • Mode层定义到什么程度才算是比较理想的解耦;
  • Model层与Presenter层如何比较优雅的相互通讯。

抱着这些问题,我本身摸索着构建出了一套个性化风格MVP架构,使用过程当中也优化了几回,现在一年多过去了再看这套架构也就算是个能用吧,因此决定新的架构优化。小程序

本文讲述了MVP的核心概念和如何从最初的乞丐版MVP架构一步步升级到平民版MVP架构,时尚版MVP架构,以及即将开始更新的旗舰版MVP架构,为了保证思路清晰,文中包含大量代码与文字,跟着文中的例子即可写出一个完整的MVP架构。api

为何用MVP架构

其实咱们平常开发中的Activity,Fragment和XML界面就至关因而一个 MVC 的架构模式,Activity中不只要处理各类 UI 操做还要请求数据以及解析。安全

这种开发方式的缺点就是业务量大的时候一个Activity 文件分分钟飙到上千行代码,想要改一处业务逻辑光是去找就要费半天劲,并且有点地方逻辑处理是同样的无奈是不一样的 Activity 就没办法很好的写成通用方法。bash

那 MVP 为啥好用呢?markdown

MVP 模式将Activity 中的业务逻辑所有分离出来,让Activity 只作 UI 逻辑的处理,全部跟Android API无关的业务逻辑由 Presenter 层来完成。网络

将业务处理分离出来后最明显的好处就是管理方便,可是缺点就是增长了代码量。架构

MVP 理论知识

在MVP 架构中跟MVC相似的是一样也分为三层。异步

Activity 和Fragment 视为View层,负责处理 UI。ide

Presenter 为业务处理层,既能调用UI逻辑,又能请求数据,该层为纯Java类,不涉及任何Android API。

Model 层中包含着具体的数据请求,数据源。

三层之间调用顺序为view->presenter->model,为了调用安全着想不可反向调用!不可跨级调用!

那Model 层如何反馈给Presenter 层的呢?Presenter 又是如何操控View 层呢?看图!

MVP架构调用关系

上图中说明了低层的不会直接给上一层作反馈,而是经过 View 、 Callback 为上级作出了反馈,这样就解决了请求数据与更新界面的异步操做。上图中 View 和 Callback 都是以接口的形式存在的,其中 View 是经典 MVP 架构中定义的,Callback 是我本身加的。

View 中定义了 Activity 的具体操做,主要是些将请求到的数据在界面中更新之类的。

Callback 中定义了请求数据时反馈的各类状态:成功、失败、异常等。

乞丐版MVP架构模式的代码实现

下面咱们用 MVP 模式构造一个简易模拟请求网络的小程序。效果图以下:

成功加载到数据

加载数据失败

数据获取异常

由于是模拟网络数据请求,因此有三个请求数据的按钮分别对应成功、失败、异常三种不一样的反馈状态。

下面是Demo中的Java文件目录:

Java文件目录

Callback接口

Callback 接口是Model层给Presenter层反馈请求信息的传递载体,因此须要在Callback中定义数据请求的各类反馈状态:

public interface MvpCallback {

   /**
     * 数据请求成功
     * @param data 请求到的数据
     */
    void onSuccess(String data);

    /**
     *  使用网络API接口请求方式时,虽然已经请求成功可是由
     *  于{@code msg}的缘由没法正常返回数据。
     */
    void onFailure(String msg);

     /**
     * 请求数据失败,指在请求网络API接口请求方式时,出现没法联网、
     * 缺乏权限,内存泄露等缘由致使没法链接到请求数据源。
     */
    void onError();

    /**
     * 当请求数据结束时,不管请求结果是成功,失败或是抛出异常都会执行此方法给用户作处理,一般作网络
     * 请求时能够在此处隐藏“正在加载”的等待控件
     */
    void onComplete();

}
复制代码

Model 类

Model 类中定了具体的网络请求操做。为模拟真实的网络请求,利用postDelayed方法模拟耗时操做,经过判断请求参数反馈不一样的请求状态:

public class MvpModel {

    /**
     * 获取网络接口数据
     * @param param 请求参数
     * @param callback 数据回调接口
     */
    public static void getNetData(final String param, final MvpCallback callback){
        
        // 利用postDelayed方法模拟网络请求数据的耗时操做
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param){

                    case "normal":
                        callback.onSuccess("根据参数"+param+"的请求网络数据成功");
                        break;

                    case "failure":
                        callback.onFailure("请求失败:参数有误");
                        break;

                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }

        },2000);
    }

}

复制代码

View 接口

View接口是Activity与Presenter层的中间层,它的做用是根据具体业务的须要,为Presenter提供调用Activity中具体UI逻辑操做的方法。

public interface MvpView {

    /**
     * 显示正在加载进度框
     */
    void showLoading();

    /**
     * 隐藏正在加载进度框
     */
    void hideLoading();

    /**
     * 当数据请求成功后,调用此接口显示数据
     * @param data 数据源
     */
    void showData(String data);

    /**
     * 当数据请求失败后,调用此接口提示
     * @param msg 失败缘由
     */
    void showFailureMessage(String msg);

    /**
     * 当数据请求异常,调用此接口提示
     */
    void showErrorMessage();

}
复制代码

Presenter类

Presenter类是具体的逻辑业务处理类,该类为纯Java类,不包含任何Android API,负责请求数据,并对数据请求的反馈进行处理。

Presenter类的构造方法中有一个View接口的参数,是为了可以经过View接口通知Activity进行更新界面等操做。

public class MvpPresenter {

    // View接口
    private MvpView mView;


    public MvpPresenter(MvpView view){
        this.mView = view;
    }

    /**
     * 获取网络数据
     * @param params 参数
     */
    public void getData(String params){

        //显示正在加载进度条
        mView.showLoading();
        // 调用Model请求数据
        MvpModel.getNetData(params, new MvpCallback() {
            @Override
            public void onSuccess(String data) {
                //调用view接口显示数据
                mView.showData(data);
            }

            @Override
            public void onFailure(String msg) {
                //调用view接口提示失败信息
                mView.showFailureMessage(msg);
            }

            @Override
            public void onError() {
                //调用view接口提示请求异常
                mView.showErrorMessage();
            }

            @Override
            public void onComplete() {
                // 隐藏正在加载进度条
                mView.hideLoading();
            }
        });
    }

}
复制代码

xml布局文件

没什么好说的,直接上代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.jessewu.mvpdemo.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:text="点击按钮获取网络数据"/>


    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取数据【成功】"
        android:onClick="getData"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取数据【失败】"
        android:onClick="getDataForFailure"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="获取数据【异常】"
        android:onClick="getDataForError"
        />

</LinearLayout>
复制代码

Activity

在Activity代码中须要强调的是若是想要调用Presenter就要先实现Presenter须要的对应的View接口。

public class MainActivity extends AppCompatActivity implements MvpView  {

    //进度条
    ProgressDialog progressDialog;
    TextView text;

    MvpPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = (TextView)findViewById(R.id.text);

        // 初始化进度条
        progressDialog = new ProgressDialog(this);
        progressDialog.setCancelable(false);
        progressDialog.setMessage("正在加载数据");

        //初始化Presenter
        presenter = new MvpPresenter(this);

    }

    // button 点击事件调用方法
    public void getData(View view){
        presenter.getData("normal");
    }

    // button 点击事件调用方法
    public void getDataForFailure(View view){
        presenter.getData("failure");
    }

    // button 点击事件调用方法
    public void getDataForError(View view){
        presenter.getData("error");
    }

    @Override
    public void showLoading() {
        if (!progressDialog.isShowing()) {
            progressDialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    @Override
    public void showData(String data) {
        text.setText(data);
    }

    @Override
    public void showFailureMessage(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        text.setText(msg);
    }

    @Override
    public void showErrorMessage() {
        Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
        text.setText("网络请求数据出现异常");
    }
}
复制代码

至此,已经完整的实现了一个简易的MVP架构。

注意!以上代码中还存在很大的问题,不能用到实际开发中,下面会讨论目前存在的问题以及如何优化。

MVP中的代码复用场景

由于上节中乞丐版MVP Demo的代码只实现了一个Activity的请求操做,容易出现一个概念的混淆:

每一个Activity都须要有与它对应的一套MVP(Model,View,Presenter)吗?

答案确定是否认的!

首先不须要数据请求的Activity固然就一样不须要MVP辅助。与其余Activity中存在相同逻辑的Activity,就不须要重复生成对应的MVP。可是这个存在相同逻辑的定义,不一样的场景有不一样的说法:

需求.jpg

场景1:业务逻辑彻底相同

场景1中Activity A和Activity C都只有一个“买东西”的逻辑,属于典型的逻辑相同,因此Activity C就能够直接用Activity A写好的MVP无需再作任何处理。

场景二、3:包含部分相同业务逻辑

场景2和场景3的逻辑相似,都属于一个业务逻辑中包含另一个能够单独存在的业务逻辑,这种状况采用继承的方法便可:

继承关系.png

场景4

场景4中Activity C想要同时调用独立服务于Activity A 和 Activity B的业务逻辑,只须要将两个业务逻辑对应的Presenter分别实例化并调用业务方法便可:

private PresenterA presenterA;
private PresenterB presenterB;

...
...

private void getData(){
    presenterA.getData();
    presenterB.getData();
}

复制代码

不要忘了实现两个Presenter对应的View:

public class ActivityC extends Activity implements ViewA,ViewB{
  ...
}
复制代码

场景5

场景5属于场景3与场景4的结合体,一样须要先把A和B的业务逻辑拆分开,而后同时调用,这里就不举例子了。

总结

经过上面一揽子场景的分析,得出的第一个结论就是MVP的结构太过于繁重,因此为了不多写重复代码和往后须要进行无心义的修改,在开发前必定要设计好逻辑调用图,这样才能事半功倍。

对于上面经典的经过业务逻辑继承实现包含重复逻辑的方法,其实也能够在一个Presenter中写好完整的逻辑方法,对于不一样的Activity须要哪一个业务逻辑方法就调用哪一个,这样岂不就简单多了。可是从架构设计角度看这种作法是不严谨的,可能存在漏洞,因此为保持软件架构的健壮仍是不要偷懒的好。

平民版MVP架构 - base层顶级父类

以前说过乞丐版MVP架构模式中还存在不少问题不能应用到实际的开发中,大概存在的问题有:

  • 构架存在漏洞
  • 代码冗余量大
  • 通用性差

针对这些问题咱们须要进一步优化,单车变摩托,升级为能够在实际开发中使用的平民版MVP架构。

调用View可能引起的空指针异常

举一个例子,在上述乞丐版MVP架构中的应用请求网络数据时须要等待后台反馈数据后更新界面,可是在请求过程当中当前Activity忽然由于某种缘由被销毁,Presenter收到后台反馈并调用View接口处理UI逻辑时因为Activity已经被销毁,就会引起空指针异常。

想要避免这种状况的发生就须要每次调用View前都知道宿主Activity的生命状态。

以前是在Presenter的构造方法中获得View接口的引用,如今咱们须要修改Presenter引用View接口的方式让View接口与宿主Activity共存亡:

public class MvpPresenter {

    // View接口
    private MvpView mView;


    public MvpPresenter(){
        //构造方法中再也不须要View参数
    }

  &emsp;/**
     * 绑定view,通常在初始化中调用该方法
     */
    public void attachView(MvpView  mvpView) {
        this.mView= mvpView;
    }

    /**
     * 断开view,通常在onDestroy中调用
     */
    public void detachView() {
        this.mView= null;
    }

    /**
     * 是否与View创建链接
     * 每次调用业务请求的时候都要出先调用方法检查是否与View创建链接
     */
    public boolean isViewAttached(){
        return mView!= null;
    }


    /**
     * 获取网络数据
     * @param params 参数
     */
    public void getData(String params){

        //显示正在加载进度条
        mView.showLoading();
        // 调用Model请求数据
        MvpModel.getNetData(params, new MvpCallback() {
            @Override
            public void onSuccess(String data) {
                //调用view接口显示数据
                if(isViewAttached()){
                    mView.showData(data);
                }
                
            }

            @Override
            public void onFailure(String msg) {
                //调用view接口提示失败信息
                if(isViewAttached()){
                     mView.showFailureMessage(msg);
                }          
            }

            @Override
            public void onError() {
                //调用view接口提示请求异常
                if(isViewAttached()){
                    mView.showErrorMessage();
                } 
            }

            @Override
            public void onComplete() {
                // 隐藏正在加载进度条
                if(isViewAttached()){
                    mView.hideLoading();
                } 
            }
        });
    }

}
复制代码

上面Presenter代码中比以前增长了三个方法:

  • attachView() 绑定View引用。
  • detachView 断开View引用。
  • isViewAttached() 判断View引用是否存在。

其中attachView()detachView()是为Activity准备的,isViewAttached()做用是Presenter内部每次调用View接口中的方法是判断View 的引用是否存在。

把绑定View的方法写到Activity的生命周期中:

public class MainActivity extends Activity implements MvpView{

  MvpPresenter presenter;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化Presenter
        presenter = new MvpPresenter();
        // 绑定View引用
        presenter.attachView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 断开View引用
        presenter.detachView();
    }

}
复制代码

结合Activity构建base层

写到这里,相信大多数人都会惊讶于MVP模式代码量的巨大,冗余代码实在太多,因此接下须要为MVP中全部单元都设计一个顶级父类来减小重复的冗余代码。一样的道理,咱们也为Activity设计一个父类方便与MVP架构更完美的结合。最后将全部父类单独分到一个base包中供外界继承调用。

Callback

在乞丐版中Callback接口中的onSuccess()方法须要根据请求数据类型的不一样设置为不一样类型的参数,因此每当有新的数据类型都须要新建一个Callback,解决方法是引入泛型的概念,用调用者去定义具体想要接收的数据类型:

public interface Callback<T> {

   /**
     * 数据请求成功
     * @param data 请求到的数据
     */
    void onSuccess(T data);

    /**
     *  使用网络API接口请求方式时,虽然已经请求成功可是由
     *  于{@code msg}的缘由没法正常返回数据。
     */
    void onFailure(String msg);

     /**
     * 请求数据失败,指在请求网络API接口请求方式时,出现没法联网、
     * 缺乏权限,内存泄露等缘由致使没法链接到请求数据源。
     */
    void onError();

    /**
     * 当请求数据结束时,不管请求结果是成功,失败或是抛出异常都会执行此方法给用户作处理,一般作网络
     * 请求时能够在此处隐藏“正在加载”的等待控件
     */
    void onComplete();

}
复制代码

BaseView

View接口中定义Activity的UI逻辑。由于有不少方法几乎在每一个Activity中都会用到,例如显示和隐藏正在加载进度条,显示Toast提示等,索性将这些方法变成通用的:

public interface BaseView {
   
     /**
     * 显示正在加载view
     */
    void showLoading();

    /**
     * 关闭正在加载view
     */
    void hideLoading();

    /**
     * 显示提示
     * @param msg
     */
    void showToast(String msg);

    /**
     * 显示请求错误提示
     */
    void showErr();

    /**
     * 获取上下文
     * @return 上下文
     */
    Context getContext();
}
复制代码

BasePresenter

Presenter中可共用的代码就是对View引用的方法了,值得注意的是,上面已经定义好了BaseView,因此咱们但愿Presenter中持有的View都是BaseView的子类,这里一样须要泛型来约束:

public class BasePresenter<V extends BaseView> {

    /**
     * 绑定的view
     */
    private V mvpView;

    /**
     * 绑定view,通常在初始化中调用该方法
     */
    @Override
    public void attachView(V mvpView) {
        this.mvpView = mvpView;
    }

    /**
     * 断开view,通常在onDestroy中调用
     */
    @Override
    public void detachView() {
        this.mvpView = null;
    }

    /**
     * 是否与View创建链接
     * 每次调用业务请求的时候都要出先调用方法检查是否与View创建链接
     */
    public boolean isViewAttached(){
        return mvpView != null;
    }

    /**
     * 获取链接的view
     */
    public V getView(){
        return mvpView;
    }

复制代码

BaseActivity

BaseActivity主要是负责实现 BaseView 中通用的UI逻辑方法,如此这些通用的方法就不用每一个Activity都要去实现一遍了。

public abstract class BaseActivity extends Activity implements BaseView {

    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setCancelable(false);
    }

    @Override
    public void showLoading() {
        if (!mProgressDialog.isShowing()) {
            mProgressDialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if (mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showErr() {
        showToast(getResources().getString(R.string.api_error_msg));
    }

    @Override
    public Context getContext() {
        return BaseActivity.this;
    }

复制代码

平民版MVP架构代码实现

封装好了base层咱们的平民版MVP架构就完成了,下面再来实现一遍以前用乞丐版MVP实现的应用。

Model

public class MvpModel {

    /**
     * 获取网络接口数据
     * @param param 请求参数
     * @param callback 数据回调接口
     */
    public static void getNetData(final String param, final MvpCallback<String> callback){
        
        // 利用postDelayed方法模拟网络请求数据的耗时操做
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (param){

                    case "normal":
                        callback.onSuccess("根据参数"+param+"的请求网络数据成功");
                        break;

                    case "failure":
                        callback.onFailure("请求失败:参数有误");
                        break;

                    case "error":
                        callback.onError();
                        break;
                }
                callback.onComplete();
            }

        },2000);
    }

}

复制代码

View 接口

public interface MvpView extends BaseView{
    /**
     * 当数据请求成功后,调用此接口显示数据
     * @param data 数据源
     */
    void showData(String data);

}
复制代码

Presenter类

public class MvpPresenter extends BasePresenter<MvpView > {


    /**
     * 获取网络数据
     * @param params 参数
     */
    public void getData(String params){
        
        if (!isViewAttached()){
            //若是没有View引用就不加载数据
            return;
        }

        //显示正在加载进度条
        getView().showLoading();

        // 调用Model请求数据
        MvpModel.getNetData(params, new MvpCallback()<String> {
            @Override
            public void onSuccess(String data) {
                //调用view接口显示数据
                if(isViewAttached()){
                    getView().showData(data);
                }
            }

            @Override
            public void onFailure(String msg) {
                //调用view接口提示失败信息
                if(isViewAttached()){
                     getView().showToast(msg);
                }          
            }

            @Override
            public void onError() {
                //调用view接口提示请求异常
                if(isViewAttached()){
                    getView().showErr();
                } 
            }

            @Override
            public void onComplete() {
                // 隐藏正在加载进度条
                if(isViewAttached()){
                    getView().hideLoading();
                } 
            }
        });
    }

}
复制代码

Activity

public class MainActivity extends BaseActivity implements MvpView  {

    TextView text;

    MvpPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = (TextView)findViewById(R.id.text);

        //初始化Presenter
        presenter = new MvpPresenter();
        presenter.attachView(this);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //断开View引用
        presenter.detachView();
    }

    @Override
    public void showData(String data) {
        text.setText(data);
    }

    // button 点击事件调用方法
    public void getData(View view){
        presenter.getData("normal");
    }

    // button 点击事件调用方法
    public void getDataForFailure(View view){
        presenter.getData("failure");
    }

    // button 点击事件调用方法
    public void getDataForError(View view){
        presenter.getData("error");
    }  
}
复制代码

Fragment怎么办?

平常开发中,并非全部的UI处理都在Activity中进行,Fragment也是其中很重要的一员,那么如何将Fragment结合到MVP中呢?

实现BaseFragement作法跟BaseActivity很相似,须要注意一下Fragement与宿主Activity的连接状况就能够。

public abstract class BaseFragment extends Fragment implements BaseView {

    public abstract int getContentViewId();

    protected abstract void initAllMembersView(Bundle savedInstanceState);

    protected Context mContext;
    protected View mRootView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mRootView = inflater.inflate(getContentViewId(), container, false);
        this.mContext = getActivity();
        initAllMembersView(savedInstanceState);
        return mRootView;
    }

    @Override
    public void showLoading() {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showLoading();
    }

    @Override
    public void showLoading(String msg) {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showLoading(msg);
    }

    @Override
    public void hideLoading() {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).hideLoading();
    }

    @Override
    public void showToast(String msg) {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showToast(msg);
    }

    @Override
    public void showErr() {
        checkActivityAttached();
        ((BaseFragmentActivity) mContext).showErr();
    }

    protected boolean isAttachedContext(){
        return getActivity() != null;
    }

    /**
     * 检查activity链接状况
     */
    public void checkActivityAttached() {
        if (getActivity() == null) {
            throw new ActivityNotAttachedException();
        }
    }

    public static class ActivityNotAttachedException extends RuntimeException {
        public ActivityNotAttachedException() {
            super("Fragment has disconnected from Activity ! - -.");
        }
    }

}
复制代码

时尚版MVP架构 - Model层的单独优化

在从乞丐版MVP架构优化成平民版MVP架构的过程当中,几乎每一个单元都作了很大优化并封装到了base层,可是惟独Model层没什么变化。因此,时尚版MVP架构的优化主要就是对Model层的优化。

Model层相比其余单元来讲比较特殊,由于它们更像一个总体,只是单纯的帮上层拿数据而已。再就是MVP的理念是让业务逻辑互相独立,这就致使每一个的网络请求也被独立成了单个Model,不光不必这么作并且找起来贼麻烦,因此时尚版MVP架构中Model层被总体封装成了庞大且独立单一模块。

优化以后的Model层是一个庞大并且独立的模块,对外提供统一的请求数据方法与请求规则,这样作的好处有不少:

  • 数据请求单独编写,无需配合上层界面测试。
  • 统一管理,修改方便。
  • 实现不一样数据源(NetAAPI,cache,database)的无缝切换。

未完待续

下篇会完善时尚版MVP架构,以及最新的旗舰版MVP架构设计,敬请期待~

旗舰版MVP架构图
相关文章
相关标签/搜索