MVP架构

MVP架构

1、MVP概述

​ MVP的全称为Model-View-Presenter,Model提供数据,View负责显示,Controller/Presenter负责逻辑的处理。

优势:

  1. View与Model完全隔离
  2. Presenter与View的具体实现技术无关
  3. 可以进行View的模拟测试

缺点:

  1. MVP的明显缺点是增加了代码的复杂度,特别是针对小型Android应用的开发,会使程序冗余。
  2. 还有大量的View->Model,Model->View的手动同步逻辑,会导致Presenter臃肿,维护困难。

MVP架构的运行图:

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

在这里插入图片描述

  • Callback(接口):定义了数据请求中的各种反馈状态。
    • 数据请求成功
    • 异常:虽然请求成功但是由于某种原因无法正常返回数据
    • 数据请求失败
    • 无论成功与否都会执行的方法
  • Presenter类:实现Callback接口中的方法,加载进度条。
  • View接口:为Presenter提供调用Activity中具体UI逻辑操作的方法。
  • Activity:在这个代码中需要强调的是如果想要调用Presenter就要先实现Presenter需要的对应的View接口。
  • Model类:定义了具体的网络请求操作。

​ 下层不能直接给上层反馈,只能通过view、Callback,这样就可以解决了请求数据与更新界面的异步操作。上图中View和Callback都是以接口的形式存在的,其中View是经典MVP架构中的定义,Callback是自己增加的。

2、实例

因为是模拟网络数据请求,所以有三个请求数据的按钮分别对应成功、失败、异常三种不同的反馈状态。

Callback接口:

Callback 接口是Model层给Presenter层反馈请求信息的传递载体,所以需要在Callback中定义数据请求的各种反馈状态。

package com.mao.mvpdemo;

//Callback接口是Model层给Presenter层反馈请求信息的传递载体,所以需要在Callback中定义数据请求各种反馈状态
public interface MvpCallback {
    /**
     * 数据请求成功
     * @param data 请求到的数据
     */
    void onSuccess(String data);

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

    /**
     * 请求数据失败,指在请求网络API接口请求方式时,出现无法联网、
     * 缺少权限,内存泄露等原因导致无法连接到请求数据源。
     */
    void onError();

    /**
     * 当请求数据结束时,无论请求结果是成功,失败或者爆出异常都会执行此方法给用户做处理,
     * 通常做网络请求时可以在此处隐藏“正在加载”的等待控件
     */
    void onComplete();
}

Model类:

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

package com.mao.mvpdemo;

import android.os.Handler;

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逻辑操作的方法。

package com.mao.mvpdemo;

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进行更新界面等操作。

package com.mao.mvpdemo;

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接口。

package com.mao.mvpdemo;

import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

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("网络请求数据出现异常");
    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }
}

3、实例:升级版

之前说过乞丐版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参数
        }
       /**
         * 绑定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包中供外界继承调用。

在平民版中MVP架构优化过程中除了Model层不动,其他的view、presenter、activity都进行了封装到了base层。

MvpCallback

在乞丐版中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提示等,索性将这些方法变成通用的:

package com.mao.testmvp2;

import android.content.Context;

/**
 * Date:2020/7/6
 */
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的子类,这里同样需要泛型来约束:

package com.mao.testmvp2;

/**
 * Author:流浪猫
 * Date:2020/7/6
 */
public class BasePresenter<V extends BaseView> {
    /**
     * 绑定的view
     */
    private V mvpView;

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

    /**
     * 断开view,一般在onDestroy中调用
     */
    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都要去实现一遍了。

package com.mao.testmvp2;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;

import androidx.annotation.Nullable;

/**
 * Author:流浪猫
 * Date:2020/7/6
 */
public class BaseActivity extends Activity implements BaseView {

    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(@Nullable 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;
    }
}

MainActivity

package com.mao.testmvp2;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import org.w3c.dom.Text;

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
    public 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点击事件调用方法
    private void getDataForFailure(View view){
        presenter.getData("failure");
    }

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

MvpModel

package com.mao.testmvp2;

import android.os.Handler;

/**
 * auther:流浪猫
 * Date:2020/7/6
 */
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);
    }
}

MvpPresenter

package com.mao.testmvp2;

/**
 * auther:流浪猫
 * Date:2020/7/6
 */
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();
                }
            }
        });
    }
}

MvpView

package com.mao.testmvp2;

/**
 * auther:流浪猫
 * Date:2020/7/6
 */
public interface MvpView extends BaseView {

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

4、完善实例

在从基础版MVP架构优化成升级版MVP架构的过程中,几乎每个单元都做了很大优化并封装到了base层,但是唯独Model层没什么变化。所以,时尚版MVP架构的优化主要就是对Model层的优化。

在这里插入图片描述

如上图所示,时尚版MVP架构的Model层中,Presenter 请求数据不再直接调用具体的Model对象,统一以 DataModel 类作为数据请求层的入口,以常量类 Token 区别具体请求。 DataModel会根据Token的不同拉取底层对应的具体Model。

单独封装,集中管理

Model层相比其他单元来说比较特殊,因为它们更像一个整体,只是单纯的帮上层拿数据而已。再就是MVP的理念是让业务逻辑互相独立,这就导致每个的网络请求也被独立成了单个Model,这种方式在实际开发中就会出现一些问题:

  • 无法对所有Model统一管理。
  • 每个Model对外提供的获取数据方法不一样,上层请求数据没有规范。
  • 代码冗余高,网络数据请求除URL和参数外其他大概都一样的。
  • 对已存在的Model管理困难,不能直观的统计已存在的Model。

所以我们更希望Model层是一个庞大且独立单一模块,请求方式规范化,管理Model更加直观。

优化之后的Model层是一个庞大而且独立的模块,对外提供统一的请求数据方法与请求规则,这样做的好处有很多:

  • 数据请求单独编写,无需配合上层界面测试。
  • 统一管理,修改方便。
  • 利用Token类可以直观的统计出已存在的请求接口。

代码实现

根据上节结构图中的描述在考虑到实际情况,我们需要设计以下几个类:

  • DataModel: 数据层顶级入口,项目中所有数据都由该类流入和流出,负责分发所有的请求数据。
  • Token:数据请求标识类,定义了项目中所有的数据请求。
  • BaseModel:所有Model的顶级父类,负责对外提供数据请求标准,对内为所有Model提供请求的底层支持。

最后实现后理想的请求数据方法是:

BaseModel

BaseModel中定义了对外的请求数据规则,包括设置参数的方法和设置Callback的方法,还可以定义一些通用的数据请求方法,比如说网络请求的Get和Post方法。

public abstract class BaseModel<T>  {    
    //数据请求参数    
    protected String[] mParams;    
    /**     
    * 设置数据请求参数     
    * @param args 参数数组     
    */    
    public  BaseModel params(String... args){        
        mParams = args;        
        return this;    
    }    
    // 添加Callback并执行数据请求    
    // 具体的数据请求由子类实现    
    public abstract void execute(Callback<T> callback);    
    // 执行Get网络请求,此类看需求由自己选择写与不写    
    protected void requestGetAPI(String url,Callback<T> callback){        
        //这里写具体的网络请求    
    }    
        // 执行Post网络请求,此类看需求由自己选择写与不写    
    protected void requestPostAPI(String url, Map params,Callback<T> callback){        
        //这里写具体的网络请求    
    }}

写好了BaseModel后再看实现具体Model的方法:

public class UserDataModel extends BaseModel<String> {    
    @Override    
    public void execute(final Callback<String> callback) {        
        // 模拟网络请求耗时操作        
        new Handler().postDelayed(new Runnable() {            
            @Override            
            public void run() {                
                // mParams 是从父类得到的请求参数                
                switch (mParams[0]){                    
                    case "normal":                        
                        callback.onSuccess("根据参数"+mParams[0]+"的请求网络数据成功");                       
                        break;                    
                    case "failure":                        
                        callback.onFailure("请求失败:参数有误");                        
                        break;                    
                    case "error":                        
                        callback.onError();                        
                        break;                
                }                
                callback.onComplete();            
            }        
        },2000);    
    }}

从上面代码段可以看出,实现具体的Model请求时必须要重写BaseModel的抽象方法execute

DataModel

由于DataModel负责数据请求的分发,所以最初打算作成一个简单工厂模式的样子,通过switch(token)语句判断要调用的Model。

但如果这样设计的话,在实际开发中我们每次添加一个数据请求接口,不光需要新建对应的Model和Token,还需要在DataModel类的switch(token)语句中新增加对应的判断,贼麻烦~

思来想去,我觉得利用反射机制会是一个比较理想的办法,请求数据时以具体Model的包名+类型作为Token,利用反射机制直接找到对应的Model。

public class DataModel {    
public static BaseModel request(String token){        
// 声明一个空的BaseModel        
    BaseModel model = null;        
    try {            
        //利用反射机制获得对应Model对象的引用            
        model = (BaseModel)Class.forName(token).newInstance();        
    } catch (ClassNotFoundException e) {            
        e.printStackTrace();        
    } catch (InstantiationException e) {            
        e.printStackTrace();        
    } catch (IllegalAccessException e) {            
        e.printStackTrace();        
    }        
    return model;    
}}

Token

由于上节中DataModel使用反射机制获取对应Model的引用,所以Token中存的就应该是对应Model的包名+类名:

public class Token {    
    // 包名    
    private static final String PACKAGE_NAME = "com.jesse.mvp.data.model.";    
    // 具体Model    
    public static final String API_USER_DATA = PACKAGE_NAME + "UserDataModel";}

使用方式

完成了Model层之后再去Presenter调用数据时的样子就舒服多了:

 DataModel    // 设置请求标识token    
     .request(Token.API_USER_DATA)    // 添加请求参数,没有则不添加    
     .params(userId)    // 注册监听回调    
     .execute(new Callback<String>() {           
         @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();           } });

添加Model的步骤

  1. 新建一个Model并继承BaseModel,完成具体的数据请求。
  2. 在Token中添加对用的Model包名+类名。注意写好注释,方便以后查阅。

5、总结

经过优化的Model层很好的统一化了请求方法规范,利用BaseModel不仅有效的减少了数据请求的冗余代码,最关键的还是得到了将所有Model的集中控制权,例如我们想给所有的请求都加上coockies,直接在BaseModel层做处理即可。

时尚版MVP虽然只对Model层进行了优化,实际开发中已经能发挥很大的作用。

下面一章旗舰版将三层同时优化。

最后

硬广一波最新开发的RecyclerView通用集合适配器SuperAdapter,它的作用是帮助开发者快速构建RecyclerView的Adapter,并封装了许多常用功能,非常好用。之前有人跟我说已经有好多人做过这个了,我想说的是我很感谢之前做过这些的大佬们,是他们给了我灵感和思路,但是不得不承认目前网上的类似项目都存在问题,实际开发中会有些问题,我在前人的思路上进行了大幅度优化,意图打造一个既好用又实用的工具。

  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值