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

Android 架构:MVP实践

程序员文章站 2022-05-12 16:15:50
...

概念

MVP分三层
M:Model包含具体的数据请求数据源,主要负责
1 :从网络,数据库,文件,传感器,等第三放数据源读取数据
2 :对外部数据类型进行解析,转换为app内部数据
3 : 对数据的临时储存,管理,协调上层数据请求

V :View主要负责处理UI,不包含页面逻辑,对应Activity和Fragment
1 :提供UI交互
2 :在presenter控制下修改UI
3 :将业务事件交由presenter处理
注意View层不储存数据,不与Model层进行交互

P :Persenter 业务处理层,既能调用UI逻辑,又能请求数据,理论上,这层为纯java类,不包含Android Api
Android 架构:MVP实践

三层调用顺序为 View→Presenter→Model 为安全着想不能反向调用,不能跨级调用
那么三层该如何交互呢?

Android 架构:MVP实践

从上图可以看出,下层会通过CallBack和View接口对上层进行反馈,上图的View和Callback,是以接口的形式存在的,View是经典MVP定义的,Callback是自己加的

View中定义了,Activity的具体操作,主要是将请求的数据进行界面更新

Callback 定义了数据请的各种状态,成功,失败,异常等

下面看一个简单的例子

View接口


public interface MvpView {
    /**
     * 定义ui处理的接口,为Presenter提供操作ui的方法
     */
    void showLoading();

    void dissMissLoading();

    void showData(String msg);
}

Callback接口

public interface MvpCallBack {
    /**
     * 定义请求数据的状态
     * @param data
     */
    void onSuccess(String data);

    void onFailure();
}

Persenter

public class MvpPresenter {

    private final MvpView mView;

    public MvpPresenter(MvpView view) {
        //和View的交互
        this.mView = view;
    }

    public void getDtata() {
        mView.showLoading();
        //和Model的交互
        MvpModel.getData(new MvpCallBack() {
            @Override
            public void onSuccess(String data) {
                mView.dissMissLoading();
                mView.showData(data);
            }

            @Override
            public void onFailure() {

            }
        });
    }
}

Model

public class MvpModel {
    /**
     * 模拟数据请求
     * @param callBack
     */
    public static void getData(final MvpCallBack callBack) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                callBack.onSuccess("请求成功");
            }
        }, 2000);
    }
}

Activity


public class MVP1Activity extends AppCompatActivity implements MvpView {

    private TextView mTextView;
    private Button mButton;
    private ProgressDialog mProgressDialog;
    private MvpPresenter mMvpPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp1);
        initView();
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setMessage("正在加载数据");
        mProgressDialog.setCancelable(false);

        mMvpPresenter = new MvpPresenter(this);

    }

    private void initView() {
        mButton = (Button) findViewById(R.id.button);
        mTextView = (TextView) findViewById(R.id.text_view);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mMvpPresenter.getDtata();
            }
        });
    }

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

    @Override
    public void dissMissLoading() {
        mProgressDialog.dismiss();
    }

    @Override
    public void showData(String msg) {
        mTextView.setText(msg);
    }
}

优化MVP

优化Persenter
1 当每次请求数据完成回调View接口的时候,可能Activity销毁,导致异常,为了避免这种情况我,我们需要把Persenter和Activity的生命周期绑定,例如:

2 抽取Persenter基类,把通用的方法抽取出来,利用泛型强制传进来的是BaseView类型

public class BasePresenter<V extends BaseView> {

    private V mView;

    public void attchView(V view) {
        this.mView = view;
    }

    public void detachView() {
        mView = null;
    }

    public V getView() {
        return mView;
    }

    public boolean isViewAttch() {
        return mView != null;
    }
}
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp2);
        initView();
        mMvpPersenter = new MvpPersenter();
        //绑定
        mMvpPersenter.attchView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //销毁
        mMvpPersenter.detachView();
    }

优化View层

1 抽取View的基类,因为很多方法在每个Activity都会用到,抽取出来,例如:显示Loading和隐藏Loading,每个具体的View接口需要继承基类并且扩展自己的方法

2 抽取BaseActivity 实现BaseView,因为这些方法是通用方法所以直接抽取出来实现,具体的Activity直接继承BaseActivity,就不用重复实现就可以直接调用,例如:直接调用显示Loading

public interface BaseView {
    void showLoading();

    void dissMissLoading();
}

public interface MvpView extends BaseView {
    void showData(String msg);
}

public class Base1Activity extends AppCompatActivity implements BaseView {

    private ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base1);
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setCancelable(false);
        mProgressDialog.setMessage("正在加载数据");
    }

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

    @Override
    public void dissMissLoading() {
        mProgressDialog.dismiss();
    }
}
public class Mvp2Activity extends Base1Activity implements MvpView {

    private Button mButton;
    private TextView mTextView;
    private MvpPersenter mMvpPersenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp2);
        initView();
        mMvpPersenter = new MvpPersenter();
        mMvpPersenter.attchView(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMvpPersenter.detachView();
    }

    private void initView() {
        mButton = (Button) findViewById(R.id.button2);
        mTextView = (TextView) findViewById(R.id.text_view2);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mMvpPersenter.getData();
            }
        });
    }

    @Override
    public void showData(String msg) {
        mTextView.setText(msg);
    }
}

优化Callback
在上面的简单版中,Callback的onSuccess(String data) 方法中,需要根据返回的不同数据类型设置不同的参数,所以每当有新的数据类型就要新建一个新的Callback ,为了解决这个问题我们引入泛型解决问题,让调用者去定义想要接受的数据类型

public interface Callback<T> {
    void onSuccess(T data);

    void onFailure();
}

优化Model层
优化后的model我们希望他能解决下面几个问题
1 当项目越来越大,model也会越来也多,不容易统计
2 无法对所有的model进行统一管理
3 每个model获取数据的方式不一样,上层没有定义统一规范
Android 架构:MVP实践
代码实现
我们需要增加这几个类
1 ModelManager :数据层*入口,项目所有的数据都由该类流入和流出,负责分发管理所有的Model
2 Token:数据请求标识类,定义项目中所有的Model,方便统计
3 BaseModel: 所有Model的*父类,负责对外提供数据请求规范,对内为所有的Modle提供底层支持

ModelManager
利用工厂模式,对外提供需要的对象

public class ModleManager {

    public static <T extends BaseModel> T request(Class<T> clazz) {
        T t = null;

        try {
            t = (T) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return t;
    }
}

Token

public class Token {
    public static final Class sMvpModleClass= MvpModle.class;
}

BaseModel

public abstract class BaseModel<T> {
    //数据参数
    public String[] params;

    public BaseModel params(String... arg) {
        params = arg;
        return this;
    }
    //添加callback并执行数据请求
    //具体数据请求由子类实现
    public abstract void excute(Callback<T> callback);

}

具体的model

public class MvpModle extends BaseModel<String> {
    //模仿数据请求
    @Override
    public void excute(final Callback<String> callback) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                callback.onSuccess(params[0]);
            }
        }, 2000);
    }
}

外部调用的形式

public class MvpPersenter extends BasePresenter<MvpView> {

    public void getData() {
        getView().showLoading();
        ModleManager
                //设置请求标识Token
                .request(Token.sMvpModleClass)
                //设置参数
                .params("更改之后")
                //执行
                .excute(new Callback<String>() {
                    @Override
                    public void onSuccess(String data) {
                        if (isViewAttch()) {
                            getView().dissMissLoading();
                            MvpView view = getView();
                            view.showData(data);
                        }
                    }

                    @Override
                    public void onFailure() {

                    }
                });
    }
}

添加一个新Model
1 继承BaseModel ,并实现具体数据请求
2 把Model添加到Token中

MVP的优缺点

优点
1 降低耦合度,实现了View和Model的完全分离,可以修改View而不影响Model
2 模块职责划分明显,层次清晰
3 Presenter 可以复用,一个Presenter可以用于多个View
4 View可以进行组件化

缺点
1 Presenter中除了应用逻辑外,还有大量的View→Model 和Model→View,的手动同步逻辑,造成Presenter比较笨重,维护起来比较困难
2 Persenter和View交互太过于频繁,以至于View一改变Presenter也需要改变
3 增加大量的类和接口

代码已经上传到GitHubgithub

参考 https://www.jianshu.com/p/9a6845b26856
http://www.jcodecraeer.com/a/anzhuokaifa/2017/1024/8636.html