这篇文章参照Hongyang的博客整理。
浅谈 MVP in Android
MVP设计模式
何为MVP
1.View 对应于Activity,负责View的绘制以及与用户交互
2.Model 依然是业务逻辑和实体模型
3.Presenter 负责完成View于Model间的交互
Presenter 在这里类似于一个主持人的角色,他负责Model实体类与VIEW表示层的交互,这个交互通常使用接口实现
MVP的特征
Presenter是整个MVP体系的控制中心,而不是单纯的处理View请求的人;
View仅仅是用户交互请求的汇报者,对于响应用户交互相关的逻辑和流程,View不参与决策,真正的决策者是Presenter;
View向Presenter发送用户交互请求应该采用这样的口吻:“我现在将用户交互请求发送给你,你看着办,需要我的时候我会协助你”,不应该是这样:“我现在处理用户交互请求了,我知道该怎么办,但是我需要你的支持,因为实现业务逻辑的Model只信任你”;
对于绑定到View上的数据,不应该是View从Presenter上“拉”回来的,应该是Presenter主动“推”给View的;
View尽可能不维护数据状态,因为其本身仅仅实现单纯的、独立的UI操作;Presenter才是整个体系的协调者,它根据处理用于交互的逻辑给View和Model安排工作。
转变成
MVC设计模式
1. View:对应于布局文件
2. Model:业务逻辑和实体模型
3. Controllor:对应于Activity
MVC与MVP的不同
MVP与MVC最大的不同是Model不能和VIEW直接通信,必须经过Persenter(主持者)
MVP 示例演示
这是模拟登陆的一个demo,使用接口实现MVP设置,在这个demo中,主Activity并没有直接调用Login的业务逻辑(Login的业务逻辑应写在user-Model中),而是使用Persenter通过接口实现login流程,通过接口实现可以进一步解耦。
代码结构
布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.mvp_pattern.MainActivity" >
<EditText
android:id="@+id/id_et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/id_et_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/id_btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login" />
<ProgressBar
android:id="@+id/id_pb_loading"
style="@android:style/Widget.ProgressBar.Inverse"
android:layout_width="50dp"
android:layout_height="50dp"
android:visibility="gone" />
</LinearLayout>
下面是Model层
User类
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
User模型的登录流程写在BIZ 包中,也属于MODEL
public interface IUserBIZ {
void login(String username, String password, OnLoginListener listener);
}
public interface OnLoginListener {
void loginSuccess(User user);
void loginFail();
}
登录流程
public class UserBIZ implements IUserBIZ {
@Override
public void login(final String username, final String password,
final OnLoginListener listener) {
// 使用子线程模拟耗时操作
new Thread() {
@Override
public void run() {
try {
sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 判断是否登录成功
if (username.equals("ytr") && password.equals("123")) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
if (listener != null) {
listener.loginSuccess(user);
}
} else {
if (listener != null) {
listener.loginFail();
}
}
};
}.start();
}
}
VIEW层,用于描述界面,需用接口进行约束,只后用于和Presenter交互
// 登录界面需实现的接口
public interface IUserLoginView {
String getUserName();
String getPassword();
void clearUserName();
void clearPassword();
void showLoading();
void hideLoading();
void toMainActivity();
void showFailError();
}
Persenter 交互核心,通过接口实现Model和View的交互
// presenter角色,是Model和View交互的桥梁
public class LoginPresenter {
private IUserBIZ mUserBIZ;
private IUserLoginView mLoginView;
// 用于在UI线程中更新UI
private Handler mHandler = new Handler();
public LoginPresenter(IUserLoginView loginView) {
mLoginView = loginView;
mUserBIZ = new UserBIZ();
}
public void login() {
mLoginView.showLoading();
mUserBIZ.login(mLoginView.getUserName(), mLoginView.getPassword(),
new OnLoginListener() {
@Override
public void loginSuccess(User user) {
// 在UI线程更新UI
mHandler.post(new Runnable() {
@Override
public void run() {
mLoginView.toMainActivity(); // 登录操作
mLoginView.hideLoading();
}
});
}
@Override
public void loginFail() {
mHandler.post(new Runnable() {
@Override
public void run() {
mLoginView.showFailError(); // 登录操作
mLoginView.hideLoading();
}
});
}
});
}
public void clear() {
mLoginView.clearPassword();
mLoginView.clearUserName();
}
}
Activity和LoginPresenter交互 M–V–P交互的实现
public class MainActivity extends ActionBarActivity implements IUserLoginView {
private EditText mEtUser;
private EditText mEtPwd;
private Button mBtnLogin;
private ProgressBar mPbLoading;
private LoginPresenter mPresenter = new LoginPresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEtUser = (EditText) findViewById(R.id.id_et_username);
mEtPwd = (EditText) findViewById(R.id.id_et_pwd);
mBtnLogin = (Button) findViewById(R.id.id_btn_submit);
mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
mBtnLogin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// 登录操作
mPresenter.login();
}
});
}
@Override
public String getUserName() {
return mEtUser.getText().toString();
}
@Override
public String getPassword() {
return mEtPwd.getText().toString();
}
@Override
public void clearUserName() {
mEtUser.setText("");
}
@Override
public void clearPassword() {
mEtPwd.setText("");
}
@Override
public void showLoading() {
mPbLoading.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
mPbLoading.setVisibility(View.GONE);
}
@Override
public void toMainActivity() {
Toast.makeText(this, "toMainActivity()", Toast.LENGTH_SHORT).show();
}
@Override
public void showFailError() {
Toast.makeText(this, "LoginFail", Toast.LENGTH_SHORT).show();
}
}