0%

mvc->mvp->mvvm的演进

最初的MVC框架

大多数页面的场景都是从服务器获取数据,然后展示。

MVC框架中的Model是retrofit+okhttp,View+Control都是在Activity/Fragment中,看下代码示例

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 void getArticles() {
if (service == null) {
service = RestClient.getInstance().getExploreApiService();
}
articleCall = service.getArticles(page);
articleCall.enqueue(new RestCallBack<Content>(listener, "article"));
}

private ResponseListener listener = new ResponseListener() {
@Override
public void onResponse(Base base, String tag) {
if (StringUtil.isEquals(base.getStatus(), "success")) {

} else {
Toast.makeText(getActivity(), base.getMessage(), Toast.LENGTH_SHORT).show();
}
refreshLayout.setRefreshing(false);
}

@Override
public void onFailure(String tag) {
articleCall = null;
refreshLayout.setRefreshing(false);
}
};

看下代码十分的方便快捷,为什么要写多几个类实现MVP框架啊,太麻烦了。

但上面的代码可能会导致下面问题

  • Activity/Fragment泄漏,这种情况发生在API请求还没返回,但Activity/Fragment已经结束。
  • 业务逻辑多的Activity/Fragment,代码量变大,维护艰难,Control代码无法复用
  • 单元测试就更难了

泄漏的问题可以在onDestroy()中调用articleCall.cancel()取消API请求来避免。经常会被遗忘的。。同时有些API请求是你不希望取消,可以继续运行的

大多数人(我也是)是只知道Activity/Fragment有生命周期,然而也就是知道而已。。没有意识到依附于Activity/Fragment产生的其他资源也应该在Activity/Fragment结束时而结束。

进化了的MVP

针对上面问题,MVP都做了解决

  • Model不能直接与View做交互了,Model只能与Presenter交互,View也只能与Presenter做交互。当Activity/Fragment结束时,调用Presenter.onDetach()断开与Presenter的连接(可能会遗忘调用),避免Activity/Fragment泄漏。onDetach()中可以取消API请求(经常会被遗忘的)
  • 代码层次明显,各层负责各自的事务,Presenter可复用
  • 易于单元测试

Presenter一般包括接口的定义与实现

1
2
3
4
5
6
7
8
9
10
11
public interface BlacklistContract {

interface View extends BaseView {
void showBlacklist(List<User> users);
}

interface Presenter extends BasePresenter<View> {
void getBlacklist();
}

}

简约的MVVM

在Activity/Fragment的周期中,当onSaveInstanceState()被调用后还更新UI可能会出现下面的异常

IllegalStateException: Can not perform this action after onSaveInstanceState with ViewPager

这个问题在MVP框架中没得到解决的。在onSaveInstanceState()后,Presenter依然会去更新UI的。

MVVM相对于MVP,是把Presenter升级成ViewModel了,ViewModel向View提供的是LiveData,同时接受View的数据输入,不再是接口或其他的回调。LiveData代表有生命周期的数据,在onSaveInstanceState()后不会去更新UI的。同时在Activity.onDestroy()后,Activity/Fragment订阅的LiveData都会被取消订阅。

不用手动调用ViewModel.onCleared(),ViewModel.onCleared()在Activity.onDestroy()时会被主动调用,我们可以在onCleared()内做清理工作,如取消API请求。

MVVM比MVP简约了很多~

真香的Kotlin协程

kotlin协程有什么香呢,我认为是结构化并发,通俗说就是有生命周期的并发,在一个协程或其子协程内开启的并发都可以随着父协程的取消而取消。

google向我们提供了viewModelScope,我们在该协程内启动的并发都会随着ViewModel的关闭而取消,不在需要手动取消API请求了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}

internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context

override fun close() {
coroutineContext.cancel()
}
}

T setTagIfAbsent(String key, T newValue)

Sets a tag associated with this viewmodel and a key.

If the given {@code newValue} is {@link Closeable},

it will be closed once {@link #clear()}

这样看来,所有依附于Activity/Fragment产生的资源在Activity/Fragment关闭时都得到了取消,能不香吗?