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

MVP模式的理解和整理

程序员文章站 2022-05-12 20:33:03
...

之前一直对MVP模式理解的不清楚,今天整理一下,理清楚。mvp模式属于Android架构设计。

一.MVP模式介绍

M-Model-模型、V-View-视图、P-Presenter-表示器。

a 、View: 视图层,对应xml文件与Activity/Fragment;(用户交互相关的操作接口定义)

b 、Presenter: 逻辑控制层,同时持有View和Model对象;(相当于view和model的传话筒)

c 、Model: 实体层,负责获取实体数据(数据操作,通过接口将数据返回给presenter层)

                       

                                                  1.  mvp模式

 

 

MVP模式的理解和整理

                                                2.mvp原理图

 

二.MVP模式优点

MVP模式的理解和整理

 

 

三.MVP模式代码

一、基类

model基类,暂时没有什么东西,

/**
 * model的基类,进行数据获取与传输,presenter持有其引用,调用对应子类的方法
 * 通过接口返回数据给 presenter
 */

public abstract class BaseModel {

}

view基类,有一些常用的方法,放在baseActivity里面实现,需要注意的是setPresenter方法,作用是在activity里面绑定对应的presenter,

public interface BaseView<P extends BasePresenter> {
    //通用的常见view互动方法,写在基类里面
    void showLoading();

    void hideLoading();

    void showError(String msg, int code);

    //view绑定presenter的方法,由baseactivity类来实现
    void setPresenter(P p);
}

presenter基类:这里面注释掉的是使用手动回收view对象的方式,后来改成用弱引用了,更优化,防止内存泄漏。

presenter里面有持有model对象,怎么初始化这个model对象,我想了好久,对比了好几个方法,刚开始是在presenter的构造方法里面,传递过来model对象,但是presenter的初始化,是在对应的activity里面,那样activity里面就要有model对象,虽然没有操作model的方法,但是感觉持有对象了,也不算完全解耦了,最后找到了这个方法,getGenericSuperclass,可以直接获取泛型参数类型的真实类型,反射出new 对象。

我的presenter对象是在对应的activity里面初始化的,没有直接在activity基类里面实例化,而是哪个activity需要对应的presenter的时候,在对应activity里面初始化,因为感觉有些小的activity里面不需要presenter等等,很简单的,就不需要都写了。

/**
 * presenter基类,持有view,model的引用,进行逻辑处理
 * 作为view和model的传话筒,持有 activity的应用,并且setpresenter使activity持有presenter的引用
 * <p>
 * presenter持有activity的引用,
 * 可以用根据绑定的activity周期,将activity引用手动制空的方式回收,
 * 还可以使用weakReference的方式。
 */

public abstract class BasePresenter<V extends BaseView, M extends BaseModel> {

    private M model;
    //    private V view;

    public WeakReference<V> mViewRef;//view持有activity的引用,防止内存泄漏,使用弱引用


    public BasePresenter(V view) {
      //  this.model = CreateUtil.getT(this, 1);
//通过反射获取model对象的方法不行,继承关系太多,获取不到相应的对象

// this.view = view; mViewRef = new WeakReference<V>(view); mViewRef.get().setPresenter(this); } public M getModel() { return model; } public V getView() { if (isAttach()) { return mViewRef.get(); } else { return null; } } public void onDetatch() { if (null != mViewRef) { mViewRef.clear(); mViewRef = null; } } private boolean isAttach() { return null != mViewRef && null != mViewRef.get(); } // public V getView() { // return view; // }// 和baseActivity里的生命周期绑定 public void onCreate() { } public void onStart() { } public void onResume() { } public void onPause() { } public void onStop() { } public void onDestroy() { model = null; // view = null; }}

/**
 * 内部获取第i个类型参数的真实类型 ,反射new出对象.但是最后我没有用这个方法,因为获取不到父类的参数类型对象,debug调了很久也不行,
*  下次有时间再改一下
 */

public class CreateUtil {

    public static <T> T getT(Object o, int i) {
        try {
            return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}

activity基类:setPresenter  showLoading hideLoading showError方法都是提前实现的view接口的,这样activity子类就不需要在每个都实现这些都需要的方法了,下面声明周期相关的,是前面presenter里面,如过没有使用使用弱引用获取view对象的方式,手动释放view对象要使用的方法。

/**
 * activity基类
 * 对应子类实现对应view接口的方法,持有presenter的引用
 */

public abstract class BaseActivity<P extends BasePresenter> extends Activity {

    private P presenter;

    public P getPresenter() {
        return presenter;
    }

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

    public void showLoading() {

    }

    public void hideLoading() {

    }

    public void showError(String msg, int code) {

    }

    private Bundle bundle;
    private final String BUNDLE_KEY = "bundle_key";



    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bundle = getIntent().getBundleExtra(BUNDLE_KEY);
        if (bundle == null) {
            bundle = new Bundle();
        }

//        if (null != presenter) {
//            presenter.onCreate();
//        }
    }

    public void startNextStepActivity(Class<? extends BaseActivity> activity) {
        Intent intent = new Intent(this, activity);
        intent.putExtra(BUNDLE_KEY, bundle);
        startActivity(intent);
    }

    public Bundle getBundle() {
        return bundle;
    }

    @Override
    protected void onStart() {
        super.onStart();
//        if (null != presenter) {
//            presenter.onStart();
//        }
    }

    @Override
    protected void onResume() {
        super.onResume();
//        if (null != presenter) {
//            presenter.onResume();
//        }
    }

    @Override
    protected void onPause() {
        super.onPause();
//        if (null != presenter) {
//            presenter.onPause();
//        }
    }

    @Override
    protected void onStop() {
        super.onStop();
//        if (null != presenter) {
//            presenter.onStop();
//        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
//        if (null != presenter) {
//            presenter.onDestroy();
//        }
    }

}

二、实现类

以login登录的方法为例子。这里使用了contract接口,是为了代码更整洁一些,将基类放一起。

contract接口:

public interface LoginContract {

    interface View extends BaseView<Presenter> {
        void onLoginSucess(String msg);
    }


    abstract class Model extends BaseModel {
        public abstract void requesetData(String data, ModelCallBack modelCallBack);
    }

    abstract class Presenter extends BasePresenter<View, Model> {
        public Presenter(View view) {
            super(view);
        }

        public abstract void login(String name, String password);
    }

    //model向presenter返回数据的接口,方法的数据类型根据需要自定义
    //这个接口也可以整理出通用的,不用每个contract都单独定义
    interface ModelCallBack {
        void onCallBack(String msg);
    }
}

model实现类:

public class LoginModelImpl extends LoginContract.Model {

    @Override
    public void requesetData(String data, final LoginContract.ModelCallBack modelCallBack) {
        //进行网络数据操作等等,
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                modelCallBack.onCallBack("获取的数据");
            }
        }, 2000);

    }
}

presenter实现类:

 

public class LoginPresenterImpl extends LoginContract.Presenter {
   private LoginModelImpl model;
    public LoginPresenterImpl(LoginContract.View view) {
        super(view);
        model =  new LoginModelImpl();
 }

    @Override
    public void login(String name, String password) {
    model.requesetData(name + password, new LoginContract.ModelCallBack() {

@Override public void onCallBack(String msg) { getView().onLoginSucess(msg); } }); }}view的实习类:重要的是oncreate里面 new 出来的对应的presenter对象,会调用到presenter基类里面绑定view.setPresenter方法,使得activity获取到presenter的对象。下面可以直接getPresenter()方法来获取presenter对象。

public class LoginActivity extends BaseActivity<LoginContract.Presenter> implements View.OnClickListener, LoginContract.View {

    private EditText etUserName;
    private EditText etPassword;
    private Button btnLogin;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        new LoginPresenterImpl(this);//实例化对应的presenter,activity绑定presenter对象
        setTitle("登录");
    }

    private void initView() {
        etUserName = (EditText) findViewById(R.id.et_user_name);
        etPassword = (EditText) findViewById(R.id.et_password);
        btnLogin = (Button) findViewById(R.id.btn_login);

        btnLogin.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_login:
                submit();
                break;
        }
    }

    private void submit() {
        String name = etUserName.getText().toString().trim();
        if (TextUtils.isEmpty(name)) {
            Toast.makeText(this, "用户名为空", Toast.LENGTH_SHORT).show();
            return;
        }

        String password = etPassword.getText().toString().trim();
        if (TextUtils.isEmpty(password)) {
            Toast.makeText(this, "密码为空", Toast.LENGTH_SHORT).show();
            return;
        }

        getPresenter().login(name, password);
    }

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

最后,包的结构如下:

MVP模式的理解和整理

借鉴博客:

https://www.jianshu.com/p/3a17382d44de

相关标签: Android mvp