一、概述
最近对 googlesamples/android-architecture 中的 MVP-dagger 进行了学习。对照项目的 MVP-dagger 分支,对 MVP-dagger 进行了实践,不日将会在另一篇文章中进行介绍。
MVP 架构,顾名思义,Model-View-Presenter。其作用是解决 Android 的 MVC 架构中,Activity 的职责不清,过于庞杂,难以维护的缺点。
在众多对 MVP 的实践中,Presenter 常有 attachView 和 unattachView 两个方法,用以建立起 Presenter 同 View 的联系,便于在 Presenter 中对 View 的接口进行调用。
然而,Presenter 中常常有一些耗时的操作,在某些情况下(诸如用户退出对应的 View),unAttachView 被调用,此时 Presenter 才完成耗时操作,需要完成对 View 的更新。但此时由于 View 已经被解绑,Presenter 中获取到的 View 为空,若不进行判空操作,则会引起空指针异常。
在 Presenter 中如何优雅地判空?通过进一步了解 Presenter 的生命周期,能不能找到更好的解决方案?这两个问题是本文要讨论的重点!
二、Presenter 中对 View 判空
2.1 最简单直接的暴力方式
https://github.com/cnneillee/DailyZHIHU/blob/master/app/src/main/java/com/neil/dailyzhihu/presenter/TopicDetailPresenter.java
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
| public class TopicDetailPresenter extends RxPresenter<TopicDetailContract.View> implements TopicDetailContract.Presenter { private RetrofitHelper mRetrofitHelper; @Inject TopicDetailPresenter(RetrofitHelper retrofitHelper) { this.mRetrofitHelper = retrofitHelper; } @Override public void getTopicDetailData(int topicId) { mRetrofitHelper.fetchTopicNewsList(topicId).enqueue(new Callback<TopicStoryListBean>() { @Override public void onResponse(Call<TopicStoryListBean> call, Response<TopicStoryListBean> response) { if (response.isSuccessful()) { mView.showContent(response.body()); } } @Override public void onFailure(Call<TopicStoryListBean> call, Throwable t) { mView.showError(t.getMessage()); } }); } }
|
在上面这个例子中,并没有对 mView 进行判空,当网络状态不好,用户退出当前 Presenter 关联的 View,就极容易引起空指针异常。
为了避免此问题的出现,应当对 mView 进行判空操作。
1 2 3
| if(mView != null) mView.showContent(response.body()); ... if(mView != null) mView.showError(t.getMessage());
|
这是最直接了当的做法。倘若对整个项目进行如是改造,且不说编码规范和设计原则的问题,单是修改整个项目的 Presenter 就得费老鼻子劲儿,修改过程也极容易出现遗漏等问题。
暴力××不可取呀!!!
2.2 整合抽象的方式
当然了,上面的代码在代码规范和设计模式上也有一定的问题。一种更佳的方式是,不直接让子 Presenter 对 mView 进行操作,而是使用 getView 方法对 mView 进行暴露,用户使用 getView 获取绑定的 view
1 2 3
| if(getView() != null) getView().showContent(response.body()); ... if(getView() != null) getView().showError(t.getMessage());
|
2.3 优雅的判空方式
来自知乎专栏的一片文章 『极光日报 - 不要再在你的 Presenter 中检查 view != null 啦』 中介绍了一种优雅的方式,抛异常/使用第三方库。
有另一种我认为更好的方式,就是将 2.2 与 抛异常结合起来。毕竟,谁都不想为了一些小的细节,而引入一个第三方库。
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
| public class TopicDetailPresenter extends RxPresenter<TopicDetailContract.View> implements TopicDetailContract.Presenter { private RetrofitHelper mRetrofitHelper; @Inject TopicDetailPresenter(RetrofitHelper retrofitHelper) { this.mRetrofitHelper = retrofitHelper; } @Override public void getTopicDetailData(int topicId) { mRetrofitHelper.fetchTopicNewsList(topicId).enqueue(new Callback<TopicStoryListBean>() { @Override public void onResponse(Call<TopicStoryListBean> call, Response<TopicStoryListBean> response) { if (response.isSuccessful()) { getView().showContent(response.body()); } } @Override public void onFailure(Call<TopicStoryListBean> call, Throwable t) { getView().showError(t.getMessage()); } }); } }
|
在父 Presenter 中
1 2 3 4
| protected View getView(){ if(mView == null) throw new IllegaStateException("view not attached"); else return mView; }
|
三、Presenter 的生命周期
这个话题源自一篇文章 『Android:聊聊 MVP 中 Presenter 的生命周期』
当然,这篇文章涵盖了处理 Presenter 的生命周期 与 Activity/Fragment 生命周期同步的问题的几个框架。同步 Presenter 和 Activity/Fragment 生命周期,从而保证在 View 层(这里姑且 Activity/Fragment 归类到 View 层吧)生命结束后,Presenter 也被终止生命,故而避免了空指针异常的问题!
这里只引用文中的几个框架,详细分析内容,可参见原文!
此文在我的 cnblogs 上同步发布,地址为:http://www.cnblogs.com/neillee/p/6800758.html