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

无DI且遵循Clean Architecture时,MVVM中如何向Repository传Context?

问题描述

我需要在Room对应的Repository中初始化Room,但该操作需要Context。如果通过ViewModel传递Context,会收到「This field leaks a context object」的内存泄漏提示。

之前看到过一种方案是在视图层初始化Repository对象,但这不符合Clean Architecture的要求——视图层不应该知晓数据层的相关内容。想请教:不使用依赖注入(DI)的情况下,该如何将Context安全传递到数据层?

现有代码示例

ViewModel代码

class MainViewModel(private val context: Context) : ViewModel() {

    private val roomManager : RoomManager = RoomManagerImpl(context)

    private fun addItem(){
        roomManager.addItem()
    }
}

Repository代码

class RoomManagerImpl(private val context: Context) : RoomManager {
    private val db = Room.databaseBuilder(
        context,
        AppDatabase::class.java, "database-name"
    ).build()
}
解决方案

不使用DI的核心思路是避免传递Activity/Fragment的Context,改用生命周期与应用一致的Application Context,同时通过合理封装让数据层安全获取Context,且不违反Clean Architecture分层原则。

方法1:通过自定义Application提供全局Context

  1. 创建自定义Application类,保存全局的Application Context:
class MyApp : Application() {
    companion object {
        lateinit var instance: MyApp
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

记得在AndroidManifest.xml的<application>标签中指定android:name=".MyApp"。

  1. 修改Repository,直接从MyApp获取Application Context初始化Room:
class RoomManagerImpl : RoomManager {
    private val db = Room.databaseBuilder(
        MyApp.instance,
        AppDatabase::class.java, "database-name"
    ).build()

    // 实现RoomManager的业务方法...
}
  1. ViewModel不再需要传递Context,直接初始化Repository:
class MainViewModel : ViewModel() {

    private val roomManager : RoomManager = RoomManagerImpl()

    private fun addItem(){
        roomManager.addItem()
    }
}

这种方式下,视图层完全不知道数据层的实现细节,符合Clean Architecture要求,同时Application Context不会造成内存泄漏。

方法2:封装数据库单例

把Room数据库的初始化逻辑封装成单例,内部使用Application Context,Repository直接获取单例数据库即可:

object DatabaseProvider {
    private lateinit var db: AppDatabase

    fun init(context: Context) {
        // 强制使用Application Context
        db = Room.databaseBuilder(
            context.applicationContext,
            AppDatabase::class.java, "database-name"
        ).build()
    }

    fun getDatabase(): AppDatabase {
        check(::db.isInitialized) { "DatabaseProvider未初始化!" }
        return db
    }
}

然后在自定义Application的onCreate()中完成初始化:

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        DatabaseProvider.init(this)
    }
}

最后修改Repository:

class RoomManagerImpl : RoomManager {
    private val db = DatabaseProvider.getDatabase()

    // 实现RoomManager的业务方法...
}

ViewModel同样无需传递Context,直接初始化Repository即可。

注意事项
  • 绝对不要在ViewModel或Repository中持有Activity/Fragment的Context,仅使用Application Context,从根源避免内存泄漏。
  • 自定义Application的方式简单直接,适合小型项目;数据库单例封装的方式更灵活,方便后续扩展(比如切换数据库实现)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 22:30:31