无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
- 创建自定义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"。
- 修改Repository,直接从MyApp获取Application Context初始化Room:
class RoomManagerImpl : RoomManager { private val db = Room.databaseBuilder( MyApp.instance, AppDatabase::class.java, "database-name" ).build() // 实现RoomManager的业务方法... }
- 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
相关产品推荐
相关产品推荐

