【问题标题】:How do I prevent memory leak caused by context class in static fields如何防止静态字段中的上下文类引起的内存泄漏
【发布时间】:2021-06-04 19:19:45
【问题描述】:

帮助!我有泄漏(至少我是这么认为的……)

下面的instance 值是我从静态伴随对象到此类的“端口”。我有一些关于以某种方式使用 applicationContext 的想法(我的 TODO:评论)......因为我收到了这个内存泄漏警告......:

为了避免这个问题,同时在每次调用这些函数时不需要上下文参数(我想以某种方式将上下文存储在类中,并在它死亡时将其处理掉),有什么好的练习解决问题?

...

class Repository private constructor(
        var context: Context
)
{
    /**
     * Repository class, class for all communication between database and external
     * sources
     * */

    private val TAG = javaClass.simpleName

    val appDatabase: AppDatabase = AppDatabase.getInstance(context.applicationContext)

    //TODO: Test if better with applicationContext?

    companion object {
        private val TAG = this::class.java.simpleName

        @Volatile private var instance:Repository ?=null

        fun getInstance(context: Context)= instance
                ?: synchronized(this){
                    instance
                            ?:Repository(context).also { instance = it }
                }
 

        fun liveDataUserResult(staffId: StaffType=StaffType.DRIVER) =
            instance?.liveDataUserResult(staffId = staffId)

        fun liveDataDepartmentResult(staffId: StaffType=StaffType.DRIVER) =
            instance?.liveDataDepartmentResult(staffId = staffId)

...

        fun liveDataGalleryItemSiteNoInUserPath() = instance?.liveDataGalleryItemSiteNoInUserPath()

...

    }

    fun liveDataGalleryItemSiteNoInUserPath() =
        appDatabase.galleryItemDao().liveDataGalleryItemSiteNoInUserPath(allText = context.getString(R.string.repository_all_text))

}
 

【问题讨论】:

  • 已更改上面的 Q 以反映我的答案低于接受的答案。否则也是同样的问题。

标签: android kotlin memory-leaks android-context


【解决方案1】:

以上所有归功于 ADM 和 @Henry Twist 的出色答案(我已接受),但我将在此处发布我的结果:


class Repository private constructor(
         context: Context
)
{
    /**
     * Repository class, class for all communication between database and external
     * sources
     * */

    private val TAG = javaClass.simpleName

    val applicationContext: Context = context.applicationContext

    val appDatabase: AppDatabase = AppDatabase.getInstance(applicationContext)

    companion object {
        private val TAG = this::class.java.simpleName

        @Volatile private var instance:Repository ?=null

        fun getInstance(context: Context)= instance
                ?: synchronized(this){
                    instance
                            ?:Repository(context).also { instance = it }
                }


        fun liveDataUserResult(staffId: StaffType=StaffType.DRIVER) =
            instance?.liveDataUserResult(staffId = staffId)
        fun liveDataDepartmentResult(staffId: StaffType=StaffType.DRIVER) =
            instance?.liveDataDepartmentResult(staffId = staffId)

...
              
        fun liveDataGalleryItemSiteNoInUserPath() = instance?.liveDataGalleryItemSiteNoInUserPath()

...

    }

    fun liveDataGalleryItemSiteNoInUserPath() =
        appDatabase.galleryItemDao().liveDataGalleryItemSiteNoInUserPath(allText = applicationContext.getString(R.string.repository_all_text))
    
}

当然,Dagger2 或 Dagger-HILT 会更优雅。

【讨论】:

    【解决方案2】:

    不要在Repository 中传递Context,除非直接传递AppDatabase 实例。

    AppDatabase 在整个应用程序中用作Singleton 因此,您只需在应用程序类或其他一些Singleton 中启动它一次,然后在任何地方使用相同的Object

    如果您使用任何依赖注入框架,如 Dagger2 或 Dagger-Hilt,创建/注入依赖将很容易。

    您可以删除构造函数字段的 var 以使其工作,然后它将不会全局保存引用。感谢@Henry Twist。

    class Repository private constructor(context: Context) {
        private val TAG = javaClass.simpleName
        var appDatabase: AppDatabase = AppDatabase.getInstance(context.applicationContext)
        companion object {
            @Volatile
            private var instance: Repository? = null
            @JvmStatic
            fun getInstance(context: Context) = instance
                ?: synchronized(this) {
                    instance
                        ?: Repository(context).also { instance = it }
                }
        }
    }
    
       
    

    【讨论】:

    • 这个答案是正确的,但作为旁注,导致实际泄漏的唯一原因是将context 参数标记为var。由于您只是在其余的初始化过程中使用applicationContext,因此不会导致任何进一步的问题!
    • 同意。我更专注于消除多次调用getInstance 的需要。只需调用一次并使用相同的实例。更新答案。
    • 感谢您对这两个问题的回答,我很快就会对此进行测试(对我来说这是周六晚上(不要打字和喝酒!),我会在周日晚上左右检查...)接受/赞成然后...
    • 顺便说一句:(当我还是有点 sauber 的时候)我看到在我的 Repository 类中我有很多对 context.getString(R.string.somestring) 的引用,因为在 Repository 中“应该”没有上下文引用,我该如何应对这样,避免在调用中引用上下文。还是我还是留下了 Dagger2/Dagger-Hilt。 (无法对文本进行硬编码,因为我在一个多语言项目中)
    • 在这种情况下,您可以将应用程序上下文而不是活动上下文传递到存储库。或者你可以只设置字符串或其他资源的 I'd 而不是将其设置为字符串。
    猜你喜欢
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 2018-01-11
    • 1970-01-01
    • 2018-03-06
    • 2012-08-08
    • 1970-01-01
    相关资源
    最近更新 更多