为什么我不在 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...

相关推荐
草莓熊Lotso8 小时前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能
恋猫de小郭8 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
工程师老罗15 小时前
如何在Android工程中配置NDK版本
android
Libraeking18 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位19 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen12320 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs21 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob21 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔21 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像