为什么我不在 Android ViewModel 中直接处理异常?

ViewModel 应该不要处理异常? 更准确地说:ViewModel 不应该处理异常,它只需要"消费结果"。

本文结合实际项目中的常见做法,说明一种更清晰的职责划分: UseCase / Repository 负责处理异常,ViewModel 只关注数据与 UI 状态。

⚠️ 说明:本文中的异常模型是刻意简化的 。 重点不在"异常如何分类",而在于: 只要异常已经在下层被处理并转换成结果,ViewModel 就不应该再关心异常本身。


1. 背景:异常到底该谁来处理?

一个典型的 clean 分层大致如下:

  • UI 层(Activity / Fragment / Compose)
  • ViewModel
  • Domain 层(UseCase)
  • Data 层(Repository / DataSource / 网络、数据库等)

常见的疑问是:

ViewModel 拿到 UseCase 或 Repository 的数据,这些底层已经做了异常处理,那 ViewModel 还要再 try/catch 吗?

我的结论是:

如果 UseCase / Repository 已经把异常转换成稳定的数据结果,ViewModel 完全可以只关心"结果",而不直接处理异常对象。


2. 分层职责:谁应该干什么?

2.1 Data / Domain 层(UseCase / Repository)

核心职责:

  • 捕获所有底层异常(网络、IO、数据库、第三方 SDK 等)
  • 在内部记录日志、上报监控、决定是否重试
  • 将异常统一转换为一个稳定、简单、可被上层消费的结果

为了突出 ViewModel 的职责边界,这里使用一个极简的结果模型

一个简单的 Repository 实现示例如下:

这里已经完成了所有异常相关的工作:

  • 捕获异常
  • 屏蔽底层实现细节(网络库、异常类型)
  • 向上层只暴露一个"成功 / 失败"的结果

2.2 ViewModel 层

核心职责:

  • 调用 UseCase / Repository
  • 根据返回的结果更新 UI 状态(loading / content / error)
  • 只处理结果分支,而不是异常本身

示例代码如下:

可以看到:

  • ViewModel 完全没有 try/catch
  • 不知道发生了什么异常
  • 只知道"成功"或"失败"
  • 只关心当前应该呈现怎样的 UI 状态

3. 为什么不建议在 ViewModel 处理异常?

3.1 破坏分层,增加耦合

如果在 ViewModel 中写这种代码:

那么:

  • ViewModel 将直接依赖底层实现
  • 网络库、数据源一旦更换,ViewModel 就需要修改
  • 异常处理逻辑分散在多个 ViewModel 中,难以复用

而这些本应是 Repository / UseCase 的职责。

3.2 错误处理难以统一

当多个 ViewModel 各自处理异常时,往往会出现:

  • 相同错误在不同页面展示方式不一致
  • 重复的异常处理逻辑到处复制
  • 想统一策略(例如:失败统一提示)成本极高

将异常处理集中在 UseCase / Repository,可以保证:

  • 策略统一
  • 行为可控
  • 上层逻辑极度简单

4. 建议的整体模式(总结)

可以用一句话概括:

UseCase / Repository 负责"把异常变成结果"; ViewModel 负责"把结果变成 UI 状态"。

UseCase / Repository:

  • 捕获所有外部系统异常

  • 处理日志、监控、重试等技术细节

  • 将结果统一转换为:

    • Success(data)
    • Error

ViewModel:

  • 不接触任何异常对象
  • 不做异常分类
  • 只根据结果更新 UI

5. 结语

只要异常已经在 UseCase / Repository 层被处理并转换成结果:

  • ViewModel 就不应该再关心异常本身
  • 它的世界里只剩下:成功或失败

这样的设计带来的好处是:

  • 分层清晰,职责单一
  • ViewModel 更容易测试
  • 代码更稳定、更容易维护

如果你现在的项目中,ViewModel 里还有大量 try/catch,可以从一两个接口开始,先把异常下移到 UseCase / Repository,逐步过渡到ViewModel 只消费结果的模式。

示例:🔗 github.com/yourname/Cl...

相关推荐
Sirens.11 小时前
MySQL表设计进阶-约束范式连接索引与事务
android·数据库·mysql
zh_xuan14 小时前
安卓上 mmap读取并修改文件内容
android·mmap
二流小码农15 小时前
鸿蒙开发:实现文本渐变效果
android·ios·harmonyos
光头闪亮亮16 小时前
Fyne ( go跨平台GUI )项目实战-项目开发必备基础知识(中)
android·c++·go
光头闪亮亮16 小时前
Fyne ( go跨平台GUI )项目实战-项目开发必备基础知识(上)
android·sqlite·go
光头闪亮亮16 小时前
Fyne ( go跨平台GUI )项目实战-项目开发必备基础知识(下)
android·c++·go
Ch_champion17 小时前
2018年之前的Android 项目上使用的技术点(及第三方库)
android
龚礼鹏17 小时前
RK Android16 wifi 投屏失败问题排查
android
晓梦林19 小时前
[ACTF2020 新生赛]BackupFile学习笔记
android·笔记·学习
ITmaster073119 小时前
告别 IDE?Android CLI 来了,开发进入 AI Agent 时代
android·ide·人工智能