之前一直对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