Android下MVP在项目的实践整理

此项目源码参考:

MVPEx

设计模式讲解参考:

MVC、MVP、MVVM三种区别_Tiger的专栏-CSDN博客​​​​​​
MVP是Google开源的一个设计模式,主要是为了细分视图(View)与模型(Model)的功能,让View只做两件事:

  • 完成用户的交互;
  • 显示界面布局,同时让Model做数据的处理,业务逻辑放到另外的一个类(Presenter)中。

下面做具体分析:

  • M:M层,在项目中负责数据的处理,包括本地数据库查询,网络数据获取都在这一层中完成;
  • View:V层,在项目中是UI模块,也就是各种activity/fragment,负责绘制UI元素、与用户进行交互。
  • P:P层,在项目做为View与Model的桥梁,M跟V层不直接交互,M层在获取到数据之后,传递到P,P层再通过接口回调到View层,同样,View层的点击等事件,通过P层去通知M层去处理。

如下图所示:

这里写图片描述

 1.MVP设计模式实践

1.1MVP基类定义

MVP设计模式下P将M和V完全隔离开,P和V是双向通信,P和M之间也是双向通信,采用面向接口编程,Fragment/Activity属于V层,Fragment/Activity会实现V接口,P通过持有的V实现通知Fragment/Activity需要进行UI更新,P间接间接持有Fragment/Activity,可能会造成内存泄露,那么如何解决呢,接下来讲解一下项目中的实践?

定义Presenter接口

我们在定义Presenter时会定义attachView()和detachView方法,然后在Activity/Fragment下onCreate()方法调用Presenter.attachView()关联持有的V(Activity/Fragment),在onDestroy()方法下调用Presenter.detachView()取消关联持有的V(Activity/Fragment),从而解决内存泄漏问题

public interface MvpPresenter<V extends MvpView> {
    @UiThread
    void attachView(V view);

    @UiThread
    void detachView(boolean retainInstance);
}

Presenter接口实现

Presenter持有V的弱引用

public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V>{
    private WeakReference<V> viewRef;

    public void MvpBasePresenter(){
    }

    @UiThread
    public void attachView(V view) {
        this.viewRef = new WeakReference(view);
    }

    @UiThread
    public V getView(){
        return this.viewRef == null ? null : (V)this.viewRef.get();
    }

    @UiThread
    public void detachView(boolean retainInstance) {
        if(this.viewRef.get() != null){
            this.viewRef.clear();
            this.viewRef = null;
        }
    }
}

定义Activity基类

添加和移除Presenter对V的引用

public abstract class BaseMvpActivity<V extends MvpView, P extends MvpPresenter<V>>
        extends AppCompatActivity implements MvpView, MvpDelegateCallback<V, P> {

    protected P presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        presenter = createPresenter();
        if(presenter != null){
            //Presenter关联V
            presenter.attachView((V) this);
        }
    }

    @NonNull
    @Override
    public abstract P createPresenter();

    @Override
    public P getPresenter() {
        return presenter;
    }

    @Override
    public void setPresenter(P presenter) {
        this.presenter = presenter;
    }

    @NonNull
    public V getMvpView() {
        return (V) this;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(presenter != null){
            //Presenter取消关联V
            presenter.detachView(false);
        }
    }
}

定义MvpView通信P和V之间通信接口基类

public interface MvpView {
}

定义MVP下P和V的代理类

public interface MvpDelegateCallback<V extends MvpView, P extends MvpPresenter<V>> {
    @NonNull
    P createPresenter();

    P getPresenter();

    void setPresenter(P presenter);

    V getMvpView();
}

1.2继承MVP基类完成业务实现

定义P和V相关的通信接口

public class MainContract {
    public interface MainPresenter extends MvpPresenter<MainView> {
        void requestData();
    }

    public interface MainView extends MvpView{
        void orderSuccess();
        void orderFail();
    }
}

Presenter实现

public class MainHomePresenter extends MvpBasePresenter<MainContract.MainView> implements MainContract.MainPresenter {

    private OrderModel orderModel;

    public MainHomePresenter(){
        orderModel = new OrderModel();
    }

    @Override
    public void requestData() {
        orderModel.order(new OrderModel.OrderStatusListener() {
            @Override
            public void orderSuccess() {
                //请求接口
                //更新UI
                getView().orderSuccess();
            }

            @Override
            public void orderFail() {
                getView().orderFail();
            }
        });
    }
}

Activity/Fragment实现View接口

public class MainActivity extends BaseMvpActivity<MainContract.MainView,
        MainContract.MainPresenter> implements MainContract.MainView {

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

    @NonNull
    @Override
    public MainContract.MainPresenter createPresenter() {
        return new MainHomePresenter();
    }

    @Override
    public void orderSuccess() {
        //更新UI
        Toast.makeText(this,"加载数据成功", Toast.LENGTH_LONG).show();
    }

    @Override
    public void orderFail() {
        //显示加载失败
    }
}

2.通过手动添加和移除对View的引用MVP设计总结

优点

1)解决了View的内存泄露问题,不产生直接强引用就能相互调用,所以不影响内存回收;

2)只需要关注业务的开发,针对接口去设计Model;

public interface OrderModel{

@POST("order")
    OrderBean order(@Query("orderNo") String orderNo);
}

3)只需要在presenter里面处理业务流程,actvity处理业务的发起和结果接受;model按照接口文档傻瓜式写上就可以了,也不需要加各种try catch;底层会帮业务处理好,直接回调给当前的View;

缺点

这样设计无法实现实现View对应多个Presenter的情况,那么接下来讲解基于反射的方式实现MVP

3.基于反射的方式实现MVP

3.1MVP相关基类定义

创建Evm中间类,让present和View、model中间,不产生强引用关系,所以也就不会产生内存泄露的问题;再就是present和modle都可以自由定义,想定义几个就定义几个,都可以通过EVM中间类进行关联,而且采用的是反射的方法,只获取接口,所以对性能无影响,不像EventBus和ButterKnife那样要遍历所有的方法或者变量,而且如果View已经回收,则会生成一个临时该接口类型的对象,不需要是否为空判断;

EVM负责管理所有的Presenter和View

public class EVM {
    private EVM(){
    }

    private Map<String, EasyView> views = new HashMap();
    private Map<String, EasyPresenter> presenters = new HashMap();

    private <T extends EasyView> T getView(Class<T> clazz){
        EasyView easyView = views.get(clazz.getSimpleName());
        if(easyView == null){
            try {
                return clazz.newInstance();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
        return (T)easyView;
    }

    private <T extends EasyPresenter> T getPresenter(Class<T> clazz){
        EasyPresenter presenter = presenters.get(clazz);
        if(presenter == null){
            try {
                return clazz.newInstance();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
        return (T)presenter;
    }

    private void managerView(EasyView easyView, boolean registerOrNot){
        //getInterfaces()方法和Java的反射机制有关,它能够获得这个对象所实现的所有接口
        Class[] classes = easyView.getClass().getInterfaces();
        for(Class clazz: classes){
            /**
             * 如果是A.isAssignableFrom(B)
             * 确定一个类(B)是不是继承来自于另一个父类(A),一个接口(A)是不是
             * 实现了另外一个接口(B),或者两个类相同。主要,这里比较的维度不是实例对象,
             * 而是类本身,因为这个方法本身就是Class类的方法,判断的肯定是和类信息相关的。
             *
             * 也就是判断当前的Class对象所表示的类,是不是参数中传递的Class对象所表示的
             * 类的父类,超接口,或者是相同的类型。是则返回true,否则返回false。
             */
            if(EasyView.class.isAssignableFrom(clazz)){
                if(registerOrNot){
                    views.put(clazz.getSimpleName(), easyView);
                }else{
                    views.remove(clazz.getSimpleName());
                }
            }
        }
    }

    public static void register(EasyView easyView){
        EVM.ins().managerView(easyView, true);
    }

    public static void unregister(EasyView easyView){
        EVM.ins().managerView(easyView, false);
    }

    public static <T extends EasyView> T getV(Class<T> clazz){
        return EVM.ins().getView(clazz);
    }

    public static <T extends EasyPresenter> T getP(Class<T> clazz){
        return EVM.ins().getPresenter(clazz);
    }

    private static class InnerClass{
        private static EVM evm = new EVM();
    }

    private static EVM ins(){
        return InnerClass.evm;
    }
}

在BaseActivity使用

public class EasyActivity extends AppCompatActivity implements EasyView{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EVM.register(this);
    }

    @Override
    public void success() {

    }

    @Override
    public void fail() {

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EVM.unregister(this);
    }
}

 Presenter和View基类接口定义

public interface EasyPresenter {
}

public interface EasyView {
    public void success();
    public void fail();
}

3.2MVP基于基类实现

写一个LoginContract,把和登录有关的View和present都定义在里边

public interface LoginContract {
    interface Presenter extends EasyPresenter{
        void doLogin(String name, String password);
        void register(String phone, String password);
        void verificationCode(String phone);
    }

    interface LoginView extends EasyView{
        void loginSuccess(User user);
        void loginFail(int status, String message);
    }

    interface RegisterView extends EasyView{
        void registerSuccess(String name, String password);
    }

    interface GetVerificationCodeView extends EasyView{
        void getVerificationCode(String code);
    }
}

View实现

public class EasyLoginActivity extends EasyActivity implements LoginContract.LoginView {

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

    private void doLogin() {
        EVM.getP(LoginPresenter.class).doLogin("15888888888","888888");
    }

    @Override
    public void loginSuccess(User user) {
        //登录成功
    }

    @Override
    public void loginFail(int status, String message) {

    }
}

Presenter实现

public class LoginPresenter implements LoginContract.Presenter {
    private LoginModel loginModel;

    public LoginPresenter(){
        loginModel = new LoginModel();
    }

    @Override
    public void doLogin(String name, String password) {
        loginModel.login(name, password, new LoginModel.LoginStatusListener() {
            @Override
            public void loginSuccess(User user) {
                //登录成功
                EVM.getV(LoginContract.LoginView.class).loginSuccess(user);
            }

            @Override
            public void loginFail(int status, String message) {
                //登录失败
                EVM.getV(LoginContract.LoginView.class).loginFail(status,message);
            }
        });
    }

    @Override
    public void register(String phone, String password) {

    }

    @Override
    public void verificationCode(String phone) {

    }
}

3.3此种MVP设计方式的总结

彻底解除View、Present、model之间的关联,其中model、View、present各自想写几个就写几个,通过evm中间类关联,都可以互相调用;

参考:

Android开发MVP模式--项目实战_曹银飞的专栏-CSDN博客_android mvp实战


版权声明:本文为ahou2468原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。