You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MVVM架构下如何在ViewModel中获取并设置Drawable资源

问题核心

ViewModel 设计上禁止持有 Fragment、Activity、View 等短生命周期对象的引用,直接传入这类 Context 会引发内存泄漏,同时 Drawable 本身属于纯 UI 层资源,本身就不应该耦合到 ViewModel 层,以下是按推荐优先级排序的可行方案:

方案1:UI层自行处理资源加载(最推荐)

这是最符合MVVM分层职责的做法,零内存泄漏风险。ViewModel 只负责返回资源ID这类纯数据,完全不接触Drawable、Context相关的UI逻辑,资源加载、视图设置的工作全部交还给对应生命周期的Fragment/Activity处理。
代码示例:

// ViewModel 中仅持有资源ID,不做资源加载逻辑
val defaultWaypointIconRes = R.drawable.waypoints_sq_blank
// Fragment 中获取数据后自行加载Drawable,设置给marker
marker.icon = ContextCompat.getDrawable(requireContext(), viewModel.defaultWaypointIconRes)

如果需要根据业务逻辑动态切换图标,也只需要在ViewModel中判断后返回对应的资源ID即可,不需要生成Drawable实例。

方案2:使用AndroidViewModel获取全局Application Context(次选)

如果确实有场景需要在ViewModel层拿到Drawable,可以继承Jetpack自带的AndroidViewModel类,这个类自带Application级别的全局Context,生命周期和应用进程一致,不会引发内存泄漏。
注意:绝对不要在普通ViewModel中通过构造参数传入Activity/Fragment/View类型的Context,仅可使用AndroidViewModel提供的Application Context。
代码示例:

// 继承AndroidViewModel,构造函数接收Application对象
class MapViewModel(application: Application) : AndroidViewModel(application) {
    fun getDefaultWaypointIcon(): Drawable? {
        // 通过getApplication()获取全局Context加载资源
        return ContextCompat.getDrawable(getApplication(), R.drawable.waypoints_sq_blank)
    }
}

额外提醒:即使使用这个方案,也不建议在ViewModel中长期缓存Drawable实例,避免Drawable间接关联View引用引发泄漏,最好按需加载后直接传递给UI层使用。

方案3:抽象资源获取接口(适合大型项目、有单元测试要求的场景)

如果项目用了依赖注入框架,可以把资源获取能力抽象成接口,ViewModel仅依赖抽象接口不直接接触Context,既解耦,写单元测试时也可以很方便地mock依赖,不需要带Android运行环境。
代码示例:

  1. 先定义资源提供的通用接口
interface ResourceProvider {
    fun getDrawable(@DrawableRes resId: Int): Drawable?
    // 可以按需扩展getString、getColor等其他资源获取方法
}
  1. 写接口实现类,内部用全局Application Context加载资源
class ResourceProviderImpl(private val appContext: Context) : ResourceProvider {
    override fun getDrawable(resId: Int): Drawable? {
        return ContextCompat.getDrawable(appContext, resId)
    }
}
  1. 通过DI框架(Hilt/Dagger等)把ResourceProvider的实例注入到ViewModel中,调用时直接用接口方法获取Drawable即可,不需要持有任何Context引用。
避坑提醒
  • 不要为了图方便在ViewModel中持有Activity/Fragment/View的引用,配置变更(如转屏)、页面销毁时这类短生命周期对象会被回收,ViewModel持有引用会直接导致内存泄漏,甚至出现空指针崩溃。
  • Drawable属于UI渲染细节,除非必要不要下沉到ViewModel层,ViewModel的核心职责是管理业务数据和状态,UI渲染相关的逻辑尽量留在UI层处理。

内容的提问来源于stack exchange,提问作者user19109604

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:45:48