MVP模式介绍
MVP模式是MVC模式的一个演化版本,全称Model View Presenter 。MVP模式的运用越来越多,它有效的降低了View的复杂度,避免了大量的业务逻辑在View中进行处理。MVP模式解除了Model和View的耦合,使得程序有更好的扩展性,可读性以及可测试性,Model和View是通过Presenter来进行通信,通过各自的接口来实现,依赖于抽象而不是具体,所以更加的灵活,修改起来更加的方便。
MVP模式并没有标准的模式,可以有多种的实现方式,主要是通过Presenter将View和Model解耦合。
Model
负责对于数据的存取,Presenter层通过Model来获得数据
View
View一般都是Activity或者Fragment,View里面会有一个Presenter变量,来实现对于View逻辑的处理
Presenter
用于沟通View和Model使其独立开来,从Model获得数据通过Presenter交由View
MVP的实现
项目结构
首先看看写得基类Presenter
public abstract class Presenter<T> {
protected Context context;
public Reference<T> mView;
public void attach(T mView) {
this.mView = new WeakReference<>(mView);
}
public void detach() {
if (mView != null) {
mView.clear();
mView = null;
}
}
public Presenter(Context context) {
this.context = context;
}
}
这是一个泛型类,泛型类型是View所要实现的接口的类型,其中有attach和detach方法,因为在Presenter中会持有View的引用,View一般是Activity,所以在销毁Activity时需要解除与Presenter的关联,避免内存泄漏;attach是为了与Activity建立联系。
下面看看BaseMvpActivity,这是一个基类Activity
public abstract class BaseMvpActivity<V, T extends Presenter<V>> extends
Activity {
protected Context context;
private LinearLayout parentLinearLayout;
private TextView mTextViewTitle;
public T presenter;
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initContentView(R.layout.activity_base);
context = this;
// 初始化Presenter
presenter = createPresenter();
// 初始化View,View与Presenter建立关联
presenter.attach((V) this);
mTextViewTitle = (TextView) findViewById(R.id.tv_title);
}
/**
* 设置标题栏的文字
*
* @param title
*/
protected void setTitleText(String title) {
mTextViewTitle.setText(title);
}
/**
* 初始化contentview
*/
private void initContentView(int layoutResID) {
ViewGroup viewGroup = (ViewGroup) findViewById(android.R.id.content);
viewGroup.removeAllViews();
parentLinearLayout = new LinearLayout(this);
parentLinearLayout.setOrientation(LinearLayout.VERTICAL);
viewGroup.addView(parentLinearLayout);
LayoutInflater.from(this)
.inflate(layoutResID, parentLinearLayout, true);
}
@Override
public void setContentView(int layoutResID) {
LayoutInflater.from(this)
.inflate(layoutResID, parentLinearLayout, true);
}
@Override
public void setContentView(View view) {
parentLinearLayout.addView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
parentLinearLayout.addView(view, params);
}
@Override
protected void onDestroy() {
presenter.detach();
super.onDestroy();
}
// 实例化presenter
public abstract T createPresenter();
}
BaseMVPActivity含有两个泛型类型,第一个是View(Activity)的接口类型,第二个是Presenter的类型。在onCreate中,创建了Presenter,并通过attach方法使得Presenter与Activity建立了联系,在onDestroy中解除了联系,避免内存泄漏。其他一些代码是一些共同逻辑的实现,以便在子Activity中复用。
Model
public class LoginModel {
public interface CallBack {
void onSuccess(String string);
void onFailure(String string);
}
public void login(final String username, final String password, final CallBack callBack) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (username.equals("lzy") && password.equals("123")) {
callBack.onSuccess("登录成功");
} else {
callBack.onFailure("登录失败");
}
}
}).start();
}
}
登录自然会有一个请求网络数据登录的方法,CallBcak接口是为了向Presenter暴露出获得的数据,使得Presenter获取Model的数据
LoginView
public interface LoginView extends BaseView {
void changeText(String text);
String getUserName();
String getPassword();
}
LoginView接口就是Activity的逻辑接口,建立Presenter和View的联系,用于在Presenter中回调,Activity(即View)中的逻辑交由Presenter处理,通过LoginView接口回调给Activity展示。
看看MainActivity
public class MainActivity extends BaseMvpActivity<LoginView, LoginPresenter> implements LoginView {
private TextView mTextView;
private EditText mEditTextUsername;
private EditText mEditTextPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
setTitleText("主界面");
Button button = (Button) findViewById(R.id.bt);
mTextView = (TextView) findViewById(R.id.tv);
mEditTextUsername = (EditText) findViewById(R.id.et_username);
mEditTextPassword = (EditText) findViewById(R.id.et_password);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.login(v);
}
});
}
@Override
public LoginPresenter createPresenter() {
return new LoginPresenter(context);
}
@Override
public void changeText(String text) {
mTextView.setText(text);
}
@Override
public String getUserName() {
return mEditTextUsername.getText().toString().trim();
}
@Override
public String getPassword() {
return mEditTextPassword.getText().toString().trim();
}
@Override
public void showLoading() {
}
@Override
public void hideLoading() {
}
@Override
public void showToast(String text) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
}
现在主界面看起来就十分的简洁了,Activity就只是负责view的显示,逻辑或数据都不再在Activity里面处理。
LoginPresenter
public class LoginPresenter extends Presenter<LoginView> {
private LoginModel loginModel;
private Handler handler;
public LoginPresenter(Context context) {
super(context);
loginModel = new LoginModel();
handler = new Handler();
}
public void login(View view) {
if (mView.get().getPassword().isEmpty() || mView.get().getUserName().isEmpty()) {
mView.get().showToast("用户名或密码不能为空");
return;
}
mView.get().changeText("正在登录");
loginModel.login(mView.get().getUserName(), mView.get().getPassword(), new LoginModel.CallBack() {
@Override
public void onSuccess(final String string) {
handler.post(new Runnable() {
@Override
public void run() {
mView.get().changeText(string);
}
});
}
@Override
public void onFailure(final String string) {
handler.post(new Runnable() {
@Override
public void run() {
mView.get().changeText(string);
}
});
}
});
}
}