之前一直对MVP模式理解的不清楚,今天整理一下,理清楚。mvp模式属于Android架构设计。
M-Model-模型、V-View-视图、P-Presenter-表示器。
a 、View: 视图层,对应xml文件与Activity/Fragment;(用户交互相关的操作接口定义)
b 、Presenter: 逻辑控制层,同时持有View和Model对象;(相当于view和model的传话筒)
c 、Model: 实体层,负责获取实体数据(数据操作,通过接口将数据返回给presenter层)
1. mvp模式
2.mvp原理图
model基类,暂时没有什么东西,
/**
* model的基类,进行数据获取与传输,presenter持有其引用,调用对应子类的方法
* 通过接口返回数据给 presenter
*/
public abstract class BaseModel {
}
view基类,有一些常用的方法,放在baseActivity里面实现,需要注意的是setPresenter方法,作用是在activity里面绑定对应的presenter,
public interface BaseView {
//通用的常见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的引用
*
* presenter持有activity的引用,
* 可以用根据绑定的activity周期,将activity引用手动制空的方式回收,
* 还可以使用weakReference的方式。
*/
public abstract class BasePresenter {
private M model;
// private V view;
public WeakReference mViewRef;//view持有activity的引用,防止内存泄漏,使用弱引用
public BasePresenter(V view) {
// this.model = CreateUtil.getT(this, 1);
//通过反射获取model对象的方法不行,继承关系太多,获取不到相应的对象
// this.view = view; mViewRef = new WeakReference
/**
* 内部获取第i个类型参数的真实类型 ,反射new出对象.但是最后我没有用这个方法,因为获取不到父类的参数类型对象,debug调了很久也不行,
* 下次有时间再改一下
*/
public class CreateUtil {
public static T getT(Object o, int i) {
try {
return ((Class) ((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 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 {
void onLoginSucess(String msg);
}
abstract class Model extends BaseModel {
public abstract void requesetData(String data, ModelCallBack modelCallBack);
}
abstract class Presenter extends BasePresenter {
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 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