ViewModel使用viewModelScope优点

3 阅读3分钟

简单回答:是的,在演示代码中,把它放进 ViewModel 确实主要是为了“偷懒”使用 viewModelScope

但往深了探讨,这涉及到 Android 架构设计中“业务逻辑该放哪” 的核心问题。你的习惯(ViewModel 只放 LiveData)其实是非常经典的 “被动式 ViewModel” 用法,而在现代架构(MVI 或 官方推荐的 MVVM)中,ViewModel 的职责稍微重一点。

我们可以从以下几个维度来探讨这个方案的合理性:

1. 为什么我首选 ViewModel + viewModelScope?

核心原因:生命周期绑定的自动取消(Auto-Cancellation)。

  • 场景:用户打开聊天页面,开始下载图片。此时用户突然退出页面(Activity/Fragment 销毁)。
  • 后果
    • 如果不加控制:下载还在后台跑,跑完回来回调更新 UI,结果 UI 没了,可能导致 Crash,或者浪费流量下载用户不再需要的东西。
    • 使用 viewModelScope:当 ViewModel 被 clear 时(页面彻底关闭),这个 Scope 下的所有协程会自动 cancel。这对于网络请求来说是极其完美的“防泄漏”机制。

如果不用 ViewModel,你就得在 Activity 的 onDestroy 里手动取消协程,或者使用 lifecycleScope(但这通常会导致旋转屏幕时任务被杀死,而 ViewModel 不会)。

2. ViewModel 不仅仅是“状态容器”

你习惯将 ViewModel 作为 LiveData 的仓库,这完全没问题。但在 Google 推荐的架构中,ViewModel 扮演的是 “UI 层的管理者”

  1. 持有状态 (State Holder):即你习惯的 LiveData/StateFlow。
  2. 处理动作 (Action Handler):响应 UI 的事件(比如点击下载)。

逻辑闭环通常是这样的:

UI 点击 -> 调用 ViewModel 的方法 -> ViewModel 启动协程 -> 调用 Repository/Manager 下载 -> 更新 LiveData -> UI 收到通知变化。

如果把 processTask 放在 ViewModel 之外(比如直接在 Activity 里调 DownloadManager),你就失去了一个“中间人”来负责把下载结果同步给 LiveData。

3. 如果你的业务非常复杂,该怎么分层?

虽然放在 ViewModel 里很方便,但如果你的 MainViewModel 里已经堆满了 LiveData,再塞进去一堆下载逻辑确实臃肿。

更优雅的工程化方案(Repository 模式):

你应该把 FileDownloadManager 的调用封装到一个 Repository (仓库层) 中,ViewModel 只是一个“发号施令”的人。

  • Repository 层:负责具体干活(查库、下载、写库)。
  • ViewModel 层:只负责调度

伪代码概念演示:

// 1. 仓库层:脏活累活我来干,不依赖 UI 生命周期
class ChatRepository {
    suspend fun downloadFiles(files: List<File>) = withContext(Dispatchers.IO) {
        // ... 调用 FileDownloadManager ...
        // ... 操作 Room ...
        return result
    }
}

// 2. ViewModel:我只负责在合适的 Scope 里喊人干活,并把结果存入 LiveData
class MainViewModel : ViewModel() {
    val downloadState = MutableLiveData<Result>() 
    val repository = ChatRepository()

    fun processTask() {
        // 使用 viewModelScope 仅仅是为了保证页面关闭时,不相关的下载能停下来
        viewModelScope.launch {
             val result = repository.downloadFiles(...)
             downloadState.value = result // 更新 UI
        }
    }
}

4. 另一种极端:完全脱离 ViewModel

如果你说:“我的下载任务很重要,用户退出聊天页面了,我还要在后台继续下载,不能取消!”

那你确实 不能 仅仅依赖 viewModelScope。此时你需要:

  1. WorkManager:这是官方推荐的持久化后台任务方案。
  2. Application Scope:自定义一个全局的 CoroutineScope(单例),生命周期跟随整个 App。

总结建议:

  • 如果你的下载是 “UI 强相关”(用户在看聊天记录,为了显示图片而下载,人走了就不下了):放 ViewModel 里用 viewModelScope 是最标准、最安全的选择
  • 如果你的下载是 “后台静默任务”(不管用户看不看,先把数据同步好):应该用 WorkManager,或者全局单例 Scope,此时确实不需要 ViewModel 参与核心逻辑。