讲解设计模式
MVP 设计模式
原理:
MVP是有MVC演变而来的,因为在Android中用MVC来写的时候,Activity到底是View层还是Controller层真的是傻傻分不去清楚,所以基本它就把这两层的活都自己一个人干了,这样一来Activity上的代码量会非常大,Controller层和View层没有实现解耦分离开来,如果后面这两层代码其中一层出了问题你就可能两层都的修改,代码是没什么重用性可言的。而MVP的出现就是为了解决这些问题的,MVP模式将Controller的工作抽取出来交给Presenter层,Presenter层负责控制处理业务逻辑,作为中间层建立起Model层和View层的联系,从而实现三层交互。这么说确实挺难让人理解的,下面就通过一个例子,在例子中夹杂着解释让我们掌握MVP的用法吧。
属性:(我就拿登录来当作例子)
- Model层:创建用于保存用户信息的实体类User(就是userbean)登录的时候用来保存用户信息,建议也保存到本地一份
1 | public class User { |
结果回调接口:
- 创建一个接口,用来通知登录是否成功(成功的话,就可以把获取到的userbean传递下去)或者 失败
- 代码展示:
1
2
3
4
5
6public interface OnLoginListener {
//登录成功的回调
void loginSuccess(User user); //登录成功后,将用户信息传递进去
//登录失败的回调
void loginFailed();
}
定义业务接口:
- 这里抽出一个接口的目的是为了降低一层耦合和便于复用。比如同一个网络请求业务你可以有OkHttp的实现,同时也可以有Retrofit的实现,这样一来就可以方便的做到网络框架的替换,当然你还可以有不同内容的实现。简单来说,就是将登录的这个功能抽取出来,成为一个接口,方便复用。
- 代码展示:
1
2
3
4
5public interface UserLogin{
//登录方法
public void login(String username, String password, OnLoginListener loginListener);
//这里就传入用户输入的账号,密码,在传入一个登录状况判断的接口
}
具体Model的实现类:
创建一个具体的类,来实现UserLogin的接口,来实现里面的方法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
26public class UserGo implements UserLogin {
@Override
public void login(final String username, final String password, final OnLoginListener loginListener) {
//模拟网络请求耗时操作
new Thread() {
@Override
public void run() {
//模拟了耗时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//模拟登录成功
if ("luoqiang".equals(username) && "108".equals(password)) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
loginListener.loginSuccess(user);
} else {
loginListener.loginFailed();
}
}
}.start();
}
}View层:
View层不关心数据,不关心逻辑处理!只关心和用户的交互,那么这个登录界面应该有的操作就是:从输入框获取用户名,获取密码,清除用户名,清除密码,登录网络请求时显示进度条,隐藏进度条,登录成功跳转到对应界面,登录失败提示。接下来定义接口如下:
这里并没有将activity来直接当作View层,原因也是为了方便复用,那个Fragment/Activity需要用到登录,就实现这个接口就行了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public interface UserLoginView {
String getUserName();
//获取用户输入的账号
String getPassword();
//获取用户输入的密码
void clearUserName();
//清除用户输入的账号
void clearPassword();
//清除用户输入的密码
void showLoading();
//显示加载(一般来说,我都会在BaseActivity/BaseFragment中实现Loading的显示和隐藏,这里就没必要在写)
void hideLoading();
//隐藏加载
void toMainActivity(User user);
//跳转到主页,顺便传递用户信息
void showFaileTips();
//登录失败的提示
}具体的用法:
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84public class UserLoginActivity extends AppCompatActivity implements UserLoginView {
private EditText mEtUsername, mEtPassword;
private Button mBtnLogin, mBtnClear;
private ProgressBar mPbLoading;
private UserLoginPresenter mUserLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//关于Presenter层,都可以写在BaseActivtiy/BaseFragment中,没必要写在Activity中
mUserLoginPresenter = new UserLoginPresenter(this);
//获取Presenter层
initViews();
}
private void initViews() {
mEtUsername = findViewById(R.id.et_username);
mEtPassword = findViewById(R.id.et_password);
mBtnClear = findViewById(R.id.btn_clear);
mBtnLogin = findViewById(R.id.btn_login);
mPbLoading = findViewById(R.id.pb_loading);
mBtnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUserLoginPresenter.login();
}
});
mBtnClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mUserLoginPresenter.clear();
}
});
}
@Override
public String getUserName() {
return mEtUsername.getText().toString();
}
@Override
public String getPassword() {
return mEtPassword.getText().toString();
}
@Override
public void clearUserName() {
mEtUsername.setText("");
}
@Override
public void clearPassword() {
mEtPassword.setText("");
}
@Override
public void showLoading() {
mPbLoading.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
mPbLoading.setVisibility(View.GONE);
}
@Override
public void toMainActivity(User user) {
Toast.makeText(this, "跳转到登录成功页面", Toast.LENGTH_SHORT).show();
}
@Override
public void showFaileTips() {
Toast.makeText(this, "登录失败", Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
//为了防止内存泄漏,解绑Presenter层对View层的引用,这里的将Presenter层置为空可以在BaseActivity/BaseFragment中进行操作
mUserLoginPresenter.detachView();
}
}
Presenter层:
- Presenter的作用就是从View层获取用户的输入,传递到Model层进行处理,然后回调给View层,输出给用户!
- Presenter一般来说,就是将View和Mode集合在一起,进行操作
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58public class UserLoginPresenter {
private UserGo userGo;
private UserLoginView userLoginView;
private Handler mHandler = new Handler();
//对应视图页面销毁的标志位,当视图销毁后回调就不需要处理了
private boolean destroyFlag;
//Presenter必须要能拿到View和Model的实现类
public UserLoginPresenter(IUserLoginView userLoginView) {
this.userLoginView = userLoginView;
this. userGo = new UserGo();
}
public void login() {
userLoginView.showLoading();
userGo.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
@Override
public void loginSuccess(final User user) {
if (!destroyFlag) { //View层销毁后不需要处理的判断
//需要在UI线程执行
mHandler.post(new Runnable() {
@Override
public void run() {
userLoginView.toMainActivity(user);
userLoginView.hideLoading();
}
});
}
}
@Override
public void loginFailed() {
if (!destroyFlag) { //View层销毁后不需要处理的判断
//需要在UI线程执行
mHandler.post(new Runnable() {
@Override
public void run() {
userLoginView.showFaileTips();
userLoginView.hideLoading();
}
});
}
}
});
}
public void clear() {
userLoginView.clearUserName();
userLoginView.clearPassword();
}
//解绑视图
public void detachView() {
destroyFlag = true;
this.userLoginView = null;
}
}