手动依赖容器中App Context为何引发内存泄漏?如何修复?
手动DI单例AppContainer的内存泄漏问题排查与修复
你的代码实现了单例模式的AppContainer用于手动依赖注入,虽然已经使用Application Context,但仍出现内存泄漏,可能的原因及无框架修复方案如下:
代码复现
internal class AppContainer private constructor(context: Context) { companion object { private var instance: AppContainer? = null fun getInstance(context: Context): AppContainer { if (instance == null) { instance = AppContainer(context.applicationContext) } return instance!! } } val resourceProvider: ResourceProvider by lazy { provideResourceProvider(context = context.applicationContext) } // 其他依赖声明...... }
可能的泄漏原因
Application Context本身是全局生命周期的,不会导致内存泄漏,问题大概率出在以下几点:
- 依赖对象持有短生命周期引用:
ResourceProvider或其他被AppContainer提供的依赖,内部可能持有Activity、Fragment、View或未注销的监听器/订阅等短生命周期对象,而AppContainer作为单例会一直持有这些依赖,导致关联的短生命周期对象无法被GC回收。 - 依赖初始化时捕获错误上下文:即使你在AppContainer构造里用了
applicationContext,如果provideResourceProvider方法内部不小心传入了非Application Context(比如某个调用方传入了Activity Context),也会引发泄漏。 - 未清理的注册类资源:如果依赖中注册了广播接收器、ContentObserver、RxJava订阅等,但没有在合适时机注销,即使持有Application Context,这些未注销的资源也会导致泄漏。
无框架修复方案
无需借助Dagger等DI框架,通过以下步骤可以修复:
- 强制校验传入的Context:在
getInstance方法中强制要求传入Application Context,避免错误的Context被传入:fun getInstance(context: Context): AppContainer { // 直接校验是否为Application Context,避免后续处理出错 require(context is Application) { "必须传入Application Context" } if (instance == null) { instance = AppContainer(context) } return instance!! } - 排查所有依赖的实现:
- 检查
ResourceProvider及其他依赖的代码,确保它们仅持有Application Context,没有引用任何短生命周期对象。 - 检查依赖中是否有未注销的监听器、订阅或广播接收器,比如如果
ResourceProvider注册了ContentObserver,需要在合适的时机(比如Application的onTerminate)注销。
- 检查
- 用弱引用处理短生命周期依赖:如果某些业务场景必须让单例依赖持有短生命周期对象(比如回调接口),改用
WeakReference持有:class ResourceProvider( private val appContext: Context, private val callbackRef: WeakReference<ResourceCallback> ) { fun loadResource() { // 使用前先检查引用是否有效 callbackRef.get()?.onResourceLoaded() } } - 定位具体泄漏点:使用LeakCanary工具生成泄漏引用链,精准定位是哪个对象被AppContainer意外持有,从而快速修复问题。
内容的提问来源于stack exchange,提问作者Igor Novikov
相关产品推荐
相关产品推荐

