Kotlin ViewModel KTX-内存泄露
ViewModel KTX 中提供了 viewModelScope ,目的是为了减少协程内存泄露。
将 GlobalScope 替换为 viewModelScope 即可。
常规情况使用协程需要手动去停止对应的协程,如果没有正确的调用则会出现内存泄露问题,而 ViewModel KTX 提供的 viewModelScope 则自动帮我们做了这件事。
从源码可以看到,内部提供了一个 CloseableCoroutineScope ,并且调用它的 close 方法即可将协程cancel。
那么关键我们需要关注什么时候调用这个 close 方法。
我们知道 ViewModel 当被清除时会回调 onClear() 方法,我们从这个方法中去找对应取消协程相关的操作。
下面是 ViewModel 的两个方法的源码。onClear()是在 clear() 中调用的,并且会调用 closeWithRuntimeException() ,在这里可以看到它会检测当前是 Closeable 类型的对应则会主动调用 close(),回到上面提到的 CloseableCoroutineScope 这个类就实现了 Closeable 接口,因此我们就找到了主动取消协程的地方了。
ViewModel KTX官方文档
本文是笔者学习之后的总结,方便日后查看学习,有任何不对的地方请指正。
在 Google Android 团队宣布了 Jetpack 的视图模型之后,MVVM 架构已经成为了 Android 开发最流行的架构之一。如下图所示:
不过在 Google 的前期官方文档中,其 Repository 层是直接使用 LiveData 的,而且连 Jetpack Room 也对 LiveData 进行了支持,接口可以直接返回 LiveData 的数据。所以在很长一段时间内,各种开源的 MVVM 框架或者博客中,也是在 Repository 层中直接使用 LiveData。
这里,我们就会有疑问:Repository 层为什么使用 LiveData 呢?(因为通过官方文档介绍,LiveData 应该要跟Acvtivity 、Fragment 这类UI组件有关系,需要依赖 Lifecycle,放在 Repository 层非常奇怪)。
那么正确的做法是什么呢?下面将会演示基于 LiveData 实践的 MVVM框架、其存在的弊端、以及基于 Flow 实践的 MVVM框架,然后通过引入 Flow 来解决 LiveData 存在的问题。
首先将这张 MVVM 框架图细化,来看看每个层级间的数据类型和数据流向:
然后再进一步细化,来看下设计细节:
以下是以 请求网络数据 为例:
UI 层:
ViewModel 层:
Repository 层:
LiveData API设计得过于简单,难以应对Repository层可能出现的许多复杂的数据处理场景。主要体现在以下三个方面:
在复杂的业务场景中,往往伴随着线程切换来对数据进行多次处理,类似 RxJava 的 observeOn 以及 Flow 的 flowOn ,而 LiveData 并没有这种能力。所以只能通过 协程 来进行线程切换,而在 Repository 层,就只能自定义 repositoryScope 并处理协程取消的逻辑。即:
LiveData 肩负着为 UI 提供数据订阅的能力,所以他的数据订阅只能在主线程,虽然可以在子线程通过 postValue 去发布数据,但短期内调用 postValue 过快,由于没有背压处理,只保留最新的数据,因此可能造成预期之外的数据丢失问题。
而 Flow 则拥有完善的背压策略,可应对 Repository 层可能出现的各种复杂数据场景。
LiveData 依赖 Lifecycle,具有生命周期感知能力,遵循 activity 和 fragment 等实体的生命周期,在非 UI 的场景中使用要么需要自定义 Lifecycle , 要么使用 LiveData#observerForever (会造成泄露的风险)。在上面的案例中,ViewModel 需要监听 Repositoy 层的 LiveData,就必须特殊处理,避免内存泄露的问题。例如:
使用 Flow 来替换 Repository 层中 LiveData 使用,主要涉及到 ViewModel 层和 Repository 层基础类的修改,而修改后的逻辑更加简洁、易读。而且官方文档也有所更新,对 LiveData 的使用场景有所限制,见: developer.android.com/topic/libra… :
由于 API 设计一致,使用方法与之前没有任何更改,因此可以无缝切换。唯一变更点就是 Repository 层的返回数据类型由 LiveData 修改为 Flow:
综上,可以在Repository层使用Flow获取数据,并且Retrofit、Room都有自带的Flow扩展支持,使用上基本无缝衔接;ViewModel层collect来自Repository层的Flow,进行数据转换,将Model转到VO,再利用LiveData进行UI更新。
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!