接触 MVP 已经很久了,但我觉得我还没有真正掌握它。它总是在不经意间给我带来意想不到的。。惊,没有喜。最早接触 MVP 是在哪里早就已经忘记了 ,只是依稀还记得对 MVP 的理解与使用经过了好几个时期。
I 黑暗时代
这是我最早接触 MVP 的时候,应该还没有毕业,只是在自己的毕设里尝试运用,没有 Contract 类,Presenter 没有继承,只有 View 与 Model 有继承,毕竟还是知道依赖于接口,而不依赖于实现的嘛。
举一个例子吧: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
47public class InboxPresenter {
private InboxViewInterface mInboxView;
private InboxModel mInboxModel;
public InboxPresenter(InboxViewInterface mInboxView) {
this.mInboxView = mInboxView;
mInboxModel = new InboxModelDBImpl();
}
public void fetchInboxs() {
}
public Long insertInbox(InboxEvent inbox) {
return mInboxModel.insertInbox(inbox);
}
public void updateInboxInTx(List<InboxEvent> inboxEvents) {
mInboxModel.updateInboxInTx(inboxEvents);
}
public void deleteInbox(InboxEvent inbox) {
mInboxModel.deleteInbox(inbox);
}
public void deletedInboxInTx(List<InboxEvent> inboxEvents) {
mInboxModel.deleteInboxInTx(inboxEvents);
}
public void loadInboxFromDB() {
mInboxModel.loadInboxByCreateTime(new DataListener<List<InboxEvent>>() {
public void onComplete(List<InboxEvent> result) {
mInboxView.showInbox(result);
}
});
}
public void loadDeletedInboxFromDB() {
mInboxModel.loadDeletedInboxByDeletedTime(new DataListener<List<InboxEvent>>() {
public void onComplete(List<InboxEvent> result) {
mInboxView.showDeletedInbox(result);
}
});
}
}
看得出来那个时候还是很粗糙的。也没有抽取 BaseXXX 之类的。
II 封建时代
这段时期我开始稍微思考下,开始使用契约类 Contract ,也尝试抽取 BaseView, BasePresenter 等,但不是有句话叫做
我当然不是上帝,不过看到自己这段时期的代码,也不禁笑了。
BaseView :1
2
3
4
5
6
7public interface BaseView<T extends BasePresenter> {
void setPresenter(T presenter);
void showLoading(boolean isLoading);
}
BasePresenter :1
2
3
4
5
6public interface BasePresenter<T extends BaseView> {
void attachView(T view);
void detachView();
}
CitiesContract :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
27public interface CityContract {
interface View extends BaseView<Presenter> {
void setCities(List<City> cities);
void setSearchCities(List<City> cities);
void setLocCity(City city);
void setCurrentCity(City currentCity);
}
interface Presenter extends BasePresenter<View> {
void requestChangeCity(String cityId);
void requestCities();
void requestCitiesWithPara(String para);
}
interface Model {
void changeCity(String cityId, DataListener<City> listener);
void getCities(DataListener<List<City>> listener);
void getcities(String para, DataListener<List<City>> listener);
}
}
然后看一个 Presenter 的实现: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
65public class CityPresenter implements CityContract.Presenter {
private CityContract.View mView;
private final CityContract.Model mModel;
private final MyTaskPool taskPool;
public CityPresenter(CityContract.View mView) {
this.mView = mView;
mView.setPresenter(this);
mModel = new CityModelImpl();
taskPool = MyTaskPool.getInstance();
}
public void attachView(CityContract.View view) {
mView = view;
}
public void detachView() {
mView = null;
}
public void requestChangeCity(String cityId) {
mModel.changeCity(cityId, new DataListener<City>() {
public void onCompleted(City result) {
mView.setCurrentCity(result);
}
});
}
public void requestCities() {
taskPool.execute(new Runnable() {
public void run() {
mModel.getCities(new DataListener<List<City>>() {
public void onCompleted(List<City> result) {
mView.setCities(result);
mView.setLocCity(new City("", "上海", "S"));
mView.setCurrentCity(new City("", "上海", "S"));
}
});
}
});
}
public void requestCitiesWithPara(final String para) {
taskPool.execute(new Runnable() {
public void run() {
mModel.getcities(para, new DataListener<List<City>>() {
public void onCompleted(List<City> result) {
mView.setSearchCities(result);
}
});
}
});
}
}
这其中最可笑的就是,上一次时期我还有点卖弄的接口依赖,这一时期就不完整了,Model 竟然是在构造方法中直接初始化的,我也不知道既然这样为什么契约类里我还要定义 Model 接口。
而且 BaseView 里的泛型也让我很迷惑,不知道这里为什么要限定一个 P 的实现,setPresenter 方法我也不知道是用来干嘛的,这些代码真的是我写的吗。。太可怕了
III 城堡时代
这个时期就是我毕业后的早期了,对于 MVP 算是有了一些比较深入
的了解吧,开发语言也逐渐转向了 Kotlin 。不过话虽如此,事实上也还是换汤不换药,Java 变成了 Kotlin,然而 MVP 的实现却好像更差了。这段时间里,我碰到了一个 P 需要对应多个 V 的情况,当时也没有多想,直接给 P 增加了一个获取单例的方法,却没想到带来了更大的问题。
1 | class LoginPresenter : LoginContract.Presenter { |
一 P 对多 V 的情况其实是比较复杂的,页面的跳转需要修改 P 所持有的 View ,什么时候需要修改 view 的具体指代,需要认真思考下,不过也只能在 View 的生命周期发生变化的时候修改:1
2
3
4
5
6
7
8
9
10override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
presenter = LoginPresenter.instance
presenter.mView = this
}
override fun onResume() {
super.onResume()
presenter.mView = this
}
为什么没有在 view 销毁的时候置空呢?
1 |
|
因为 Kotlin 的关系,这样的写法就是不可空,所以是没办法写 presenter.mView=null
的,只是,当初我为什么没有把 mView 定义成 View?
呢?
很明显,上述代码在这一个完整的 MVP 流程结束后,因为最后一个 V 仍然被 P 所引用而无法释放,会造成内存泄漏。然而在一个很长的时间内,我都没有意识到。
不仅如此,在一个 MVP 内,会有需要同时刷新多个 V 的时候,即时 V 处于未激活状态,那么这个时候一个单例的 P 该怎么来应对呢。我曾尝试过这样的做法:
1 | private val mViewSet = LinkedHashSet<MineContract.View>() |
保存所有处于存活状态的 View ,当一个 View 被销毁后,当前 View 就是上一个绑定的 View ,逻辑上来讲是没有问题的。但是对于同一个 Presenter 来说,要应对这么多 View 的请求,也有点应付不来,如果多个 View 调用了同一个方法,那么到底该把数据给谁呢?尤其是根据数据层的响应,需要刷新 UI 的时候,难道要遍历所有保存在 mViewList 的 View 来刷新 UI 吗?如果接口返回异常呢,那就全部显示接口异常的 UI 吗?这可能是一个比较难以决定的问题。
所以啊,我感觉这段时间才是 MVP 最黑暗的时候啊。
IV 帝王时代
黑暗过后,便是黎明了。认识到自己的问题,才能更好的解决问题。
或许回归初心,就是最好的结果了。当然肯定不是回到过去,而且带着一开始同样的心态,继续努力。
到了这一时期,几乎所有的问题都已浮出水面,就待解决了。而且还有了新的可能:一 V 对多 P,考验功力的时候到了…
首先解决单例 Presenter 无法同时应对多个 View 的问题:其实很简单,不要让 P 成为单例即可。虽然我们只写了一个 P 的实现,但并不代表多个 View 就要用同一个 P 对象啊,我们尽可以给每个 View 都初始化一个 P ,前提是 P 中不要有缓存数据的功能,如果想要缓存数据为什么不缓存在 Model 里呢,我觉得多个 P 使用一个 Model 完全没有问题啊。这也正是前面黑暗时代里的 MVP ,回归初心嘛。
同时这样也解决了 mViewList 最后一个 View 无法释放的问题,毕竟根本就不需要 mViewList 了。因为现在是一个 V 对应一个 P,V 被销毁的时候,关联的 P 对象便可以同时销毁,也就不会出现内存泄漏的问题了。
当然不要忘记把 mView 改成 View? 类型。
看一下现在的 MVP UML 图:
最后,期待一下后帝王时代吧。