MVP模式的理解和整理

之前一直对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模式(这个图片传不了)



                                                2.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();
    }
}

最后,包的结构如下:


借鉴博客:

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

猜你喜欢

转载自blog.csdn.net/hpp_1225/article/details/80674451