百科狗-知识改变命运!
--

Kotlin ViewModel KTX-内存泄露

百变鹏仔2年前 (2023-12-22)阅读数 12#综合百科
文章标签数据方法

ViewModel KTX 中提供了 viewModelScope ,目的是为了减少协程内存泄露。

将 GlobalScope 替换为 viewModelScope 即可。

常规情况使用协程需要手动去停止对应的协程,如果没有正确的调用则会出现内存泄露问题,而 ViewModel KTX 提供的 viewModelScope 则自动帮我们做了这件事。

从源码可以看到,内部提供了一个 CloseableCoroutineScope ,并且调用它的 close 方法即可将协程cancel。

那么关键我们需要关注什么时候调用这个 close 方法。

我们知道 ViewModel 当被清除时会回调 onClear() 方法,我们从这个方法中去找对应取消协程相关的操作。

Kotlin ViewModel KTX-内存泄露

下面是 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

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)