android架构模式之MVP

What?

Model-View-Presenter

Why?

Activity太忙了!既作为View又作为Controller,还要忙着与Model耦合
Activity一般会采用布局文件来作为视图,但大多情况下还需要在Activity中对View进行动态布局、变化,所以我说Activity承担了View的工作
Activity显然也是一个Controller,当用户与视图交互,还是Activity来处理相关视图逻辑
Activity还需要调用Model来处理相关业务逻辑,与Model也是耦合的

How?

降低Activity的职责,将其仅作为一个View,不再与Model有任何耦合
抽出Presenter,Presenter通过接口访问View,包含Model的引用;View包含Presenter的引用,间接调用Model层处理业务逻辑,见下图
MVC VS. MVP

A Simple Example

整体包图见下图,分model、view、presenter三个包
package diagram
model下VO表示Value Object,其实就是java bean,仅包含get、set方法,BO表示Bussiness Object,处理业务逻辑,
那么BO中异步完成业务后如何通知View呢?就靠这个Callback接口,由Presenter中实现

model.BO.IUserModel

1
2
3
public interface IUserModel {
public void login(LoginRequest request, Callback<LoginResponse> callback);
}

model.BO.UserModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UserModel implements IUserModel{
@Override
public void login(final LoginRequest request, final Callback<LoginResponse> callback) {
new Thread(new Runnable() {
@Override
public void run() {
String userName = request.getUserName();
String userPassword = request.getUserPassword();
//此处进行网络请求验证用户邮箱、密码是否合法
//..
boolean isValid = userName.equals("admin") && userPassword.equals("123456");
if(isValid){
//模拟从服务器获得的accessToken
callback.success(new LoginResponse("a7f7v9l0389f8k3ls9"));
}else{
//模拟从服务器获得的错误原因
callback.fail("用户名或密码错误");
}
}
}).start();
}
}

model.VO.LoginRequest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LoginRequest {
private String userName;
private String userPassword;
public LoginRequest() {
}
public LoginRequest(String userPassword, String userName) {
this.userPassword = userPassword;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
}

model.VO.LoginResponse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class LoginResponse {
private String accessToken;
public LoginResponse() {
}
public LoginResponse(String accessToken) {
this.accessToken = accessToken;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
}

model.Callback

1
2
3
4
public interface Callback<T> {
void success(T t);
void fail(String reason);
}

presenter.ILoginPresenter

1
2
3
public interface ILoginPresenter{
public void login();
}

presenter.LoginPresenter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class LoginPresenter implements ILoginPresenter {
private ILoginView loginView;
private IUserModel userModel;
private Handler handler;
public LoginPresenter(ILoginView loginView) {
this.loginView = loginView;
this.userModel = new UserModel();
this.handler = new Handler(Looper.getMainLooper());
}
@Override
public void login() {
userModel.login(new LoginRequest(loginView.getUserName(), loginView.getUserPassword()), new Callback<LoginResponse>() {
@Override
public void success(LoginResponse loginResponse) {
String accessToken = loginResponse.getAccessToken();
//此处缓存accessToken或其他操作
//..
handler.post(new Runnable() {
@Override
public void run() {
loginView.loginSuccess();
}
});
}
@Override
public void fail(final String reason) {
handler.post(new Runnable() {
@Override
public void run() {
loginView.loginFail(reason);
}
});
}
});
}
}

view.ILoginView

1
2
3
4
5
6
public interface ILoginView {
public String getUserName();
public String getUserPassword();
public void loginSuccess();
public void loginFail(String reason);
}

view.LoginActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class LoginActivity extends AppCompatActivity implements ILoginView{
private EditText userNameEditText;
private EditText userPasswordEditText;
private Button loginButton;
private ILoginPresenter loginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
loginPresenter = new LoginPresenter(this);
userNameEditText = (EditText)findViewById(R.id.username);
userPasswordEditText = (EditText)findViewById(R.id.password);
loginButton = (Button)findViewById(R.id.login);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loginPresenter.login();
}
});
}
@Override
public String getUserName() {
return userNameEditText.getText().toString();
}
@Override
public String getUserPassword() {
return userPasswordEditText.getText().toString();
}
@Override
public void loginSuccess() {
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
//跳转到其他Activity或进行其他操作
//..
}
@Override
public void loginFail(String reason) {
Toast.makeText(this, reason, Toast.LENGTH_SHORT).show();
}
}

是不是结构还挺清楚的呢?至于是否要在项目中采用MVP,其实还要看项目的规模
demo图如下
demo