在安卓开发中使用 MVP 模式已经非常普遍了,网上关于 MVP 的讲解也相当多了,不过看得再多还是自己写一遍比较熟练。
MVP 分别代表了:M(Model)、V(View)、P(Presenter)的缩写,代表了三个不同的模块。
- Model:负责处理数据的加载或者存储,比如从网络中或者本地数据库中获取数据。
- View:负责界面数据的展示,以及与用户行为的交互。
- Presenter:是模型和视图的桥梁,将模型和视图分离开来,主要负责处理事务逻辑。
如果所示,MVP 与 MVC 的不同之处在于,MVP 中的 View 层和 Model 层之间不进行交互,是通过中间的 Presenter 中来进行交互的。这样的好处就是能够解耦,降低 View 层和 Model 层之间的耦合度。
谷歌官方也给出了一个 MVP 模式的示例:https://github.com/googlesamples/android-architecture
MVP 模式实现登陆功能
用 MVP 模式简单实现了一个登陆功能,项目的包结构如下,根据模块层次来分层的。
接口定义
在 LoginContract 接口中定义了 M 层、V 层、P 层的接口,以及 Model 层数据传递到 Presenter 时的回调接口,将所有的接口放到一个总的接口中,这样不会那么的散乱,便于管理。
而且, M 层、V 层、P 层的实现类都将实现这些接口,这样的好处就是便于替换。
因为 Java 中的向上转型,当我们如下声明一个类时,LoginPresenter 类就向上转型成了 LoginContract.Presenter 类,这时,LoginPresenter 只能调用向上转型的类,也就是接口类中的定义的方法,不能再调用自己实现的其他方法,这样一样,就方便我们在需要的时候对 M 层、V 层、P 层 的类进行替换,可以替换内部的实现方法,而调用的方法名不变。
1private LoginContract.Presenter mPresenter = new LoginPresenter()
1public interface LoginContract {
2
3 /**
4 * View 层的接口
5 */
6 interface View extends BaseView<Presenter> {
7 void showLoading();
8
9 void hideLoading();
10
11 void setEmail(String email);
12
13 void setPassword(String password);
14
15 void toActivity();
16
17 boolean isActive();
18 }
19
20 /**
21 * Presenter 层的接口
22 */
23 interface Presenter extends BasePresenter {
24 void showLoadingDialog();
25
26 void cancelLoadingDialog();
27
28 void checkUserInfo(String email,String password);
29 }
30
31 /**
32 * Model 层的接口
33 */
34 interface Model {
35
36 List<String> LoaderInitData();
37
38 void checkUserInfo(String email,String password,LoginContract.CallBack callBack);
39
40 }
41
42 /**
43 * Model 层传递数据到 Presenter 层时的回调接口
44 */
45 interface CallBack {
46 void LoginSuccess();
47 void LoginFailed();
48 }
49}
V 层实现
将 Fragment 作为 MVP 中的 V 层,而 Activity 则是作为一个总的控制类,并没有做具体的业务逻辑操作,这里是仿照着谷歌官方的例子来编写的,当然也可以将 Activity 作为 V 层来实现。
在 V 层中需要绑定 P 层的 Presenter ,而 V 层则处理数据的展示和与用户的 UI 交互。
而 P 层则是负责处理具体的逻辑。
所以,V 层是将用户的事件都交给了 P 层去处理,而 P 层通过调用 M 层处理的数据,再将它展示给 V 层去。
1/**
2 * 给 View 来传入 Presenter 来绑定 presenter
3 *
4 * @param presenter
5 */
6
7@Override
8public void setPresenter(LoginContract.Presenter presenter) {
9 this.mPresenter = presenter;
10}
P 层实现
P 层是 MVP 中最重要的负责具体逻辑处理的一层,它持有 V 层和 M 层的引用,毕竟它需要调用 V 层和 M 层的方法。
由于 P 层持有了 V 层的引用,当 presenter 有后台异步的耗时操作时,如果这时退出了 V 层,而后台异步线程的操作并不会立即停止,这时候就会引发内存泄漏了。
1/**
2 * 构造方法中通过 View 的 setPresenter 让 View 获得了 Presenter 的实例
3 * 这样,在 View 中就可以对 Presenter 中的方法进行操作了
4 *
5 * @param view
6 */
7public LoginPresenter(LoginContract.View view) {
8 mView = view;
9 /**
10 * Presenter 绑定 View
11 */
12 mView.setPresenter(this);
13
14 /**
15 * Presenter 引用数据层 Model
16 */
17
18 mModel = new LoginModel();
19
20 /**
21 * 得到主线程,也就是 UI 线程的消息循环 Looper
22 */
23 UiHandler = new Handler(Looper.getMainLooper());
24}
M 层实现
M 层也实现了 LoginContract 中的 Model 接口,M 层主要负责从网络或者本地数据库中加载数据,因为安卓的主线程并不是线程安全的,而且主线程中不允许进行网络和耗时操作,所以 M 层中的操作都是在异步的子线程中进行的,这样就需要向主线程的消息循环中发送消息,获得主线程中的消息循环(Looper.getMainLooper()),从而更新主线程的 UI 。
1public class LoginModel implements LoginContract.Model {
2
3 public LoginModel() {
4 }
5}
具体的代码实现都可以在 Github 上找到,项目地址是:https://github.com/glumes/MVPLogin
注意事项
防止内存泄漏
在这里,我用的是 Fragment 作为 V 层,为了防止在 P 层中的异步耗时操作执行的时候,V 层已经退出了,所以在仿照谷歌官方的例子,在 V 层接口中定义了 isActive()
方法:
1/**
2 * 防止 P 层中持有 V 层导致内存泄漏
3 * 通过 Fragment 中的 isAdded() 方法判断当前 Fragment 是否已经添加到 Activity 当中去
4 * @return
5 */
6@Override
7public boolean isActive() {
8 return isAdded();
9}
如果是采用 Activity 作为 V 层,则可以在 P 层添加一个销毁 V 层的方法,P 层增加了类似的生命周期的方法,用来在退出 Activity 的时候取消持有的 V 层引用。
1//presenter中添加mvpView 置为null的方法public void onDestroy(){
2 mvpView = null;
3}
4
5//退出时销毁持有Activity@Overrideprotected void onDestroy() {
6 mvpPresenter.onDestroy();
7 super.onDestroy();
8}
声明 Presenter 并绑定 V 层
在 Activity 中有这样一段代码,通过 new 创建了一个 Presenter,但却没有赋值给任何对象。
在谷歌的官方例子中也是这样创建的,这是因为将 Fragment 作为 V 层,并不需要在 Activity 中处理什么逻辑操作。
1private LoginFragment mFragment;
2@Override
3protected void onCreate(Bundle savedInstanceState) {
4 super.onCreate(savedInstanceState);
5 setContentView(R.layout.activity_main);
6 mFragment = LoginFragment.newInstance();
7
8 FragmentTransaction transaction = getFragmentManager().beginTransaction();
9 transaction.replace(R.id.content,mFragment);
10 transaction.commit();
11
12 /**
13 * 声明 Presenter 的同时,将 View 和 Presenter 绑定起来
14 * 因为 Activity 是一个总控制类,所以 New 方法创建了一个 Presenter 却不需要赋值给 Activity 中的变量
15 */
16 new LoginPresenter(mFragment);
17}
通过查看代码可以得知,在 Presenter 的构造函数中,通过 mView.setPresenter(this);
将 Presenter 自身又传入了 V 层中,这样 V 层通过 setPresenter()
方法持有了 P 层的引用,这样一来,P 层和 V 层就互相持有了各自的引用 。
1public LoginPresenter(LoginContract.View view) {
2 mView = view;
3 /**
4 * Presenter 绑定 View
5 */
6 mView.setPresenter(this);
参考
- http://www.cnblogs.com/liuling/archive/2015/12/23/mvp-pattern-android.html
- http://blog.csdn.net/dantestones/article/details/51445208
- http://blog.csdn.net/lmj623565791/article/details/46596109
原创文章,转载请注明来源: 理解使用MVP架构
相关文章
留言