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

Kotlin DataStore全局单例内存泄漏,求正确实现方式

解决DataStore全局实例的内存泄漏问题

你遇到的StaticFieldLeak警告,原因是伴生对象中的静态变量持有了Application实例的强引用,静态变量的生命周期和整个应用进程绑定,当Application因配置变更等原因被销毁时,这个引用会导致旧的Application实例无法被垃圾回收,从而引发内存泄漏。

下面是几种正确的实现方式:

方式一:使用Hilt依赖注入(推荐)

依赖注入框架能自动管理对象生命周期,避免手动管理单例带来的内存泄漏问题。

  1. 配置Hilt并修改Application类
@HiltAndroidApp
class MyApplication : Application()
  1. 创建Module提供DataStore单例
@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {
    @Provides
    fun provideTokenDataStore(@ApplicationContext context: Context): DataStoreManager {
        return TokenDataStore(context)
    }
}
  1. 在需要使用的地方注入
class SomeViewModel @Inject constructor(
    private val dataStoreManager: DataStoreManager
) : ViewModel() {
    // 调用dataStoreManager的getToken/saveToken/clearToken方法
}

方式二:手动实现安全的懒加载单例

如果不使用依赖注入,需要确保单例持有Application Context而非Activity/Application实例本身,同时用双重校验锁保证线程安全。

首先修改DataStore类(建议重命名避免和Jetpack的DataStore类冲突):

interface DataStoreManager {
    suspend fun getToken(): String
    suspend fun saveToken(token: String)
    suspend fun clearToken()
}

class TokenDataStore private constructor(private val context: Context) : DataStoreManager {
    private val Context.dataStore by preferencesDataStore(name = "settings")

    override suspend fun getToken(): String = withContext(Dispatchers.IO) {
        val preferences = context.dataStore.data.first()
        preferences[PreferencesKeys.TOKEN] ?: ""
    }

    override suspend fun saveToken(token: String) = withContext(Dispatchers.IO) {
        context.dataStore.edit { it[PreferencesKeys.TOKEN] = token }
    }

    override suspend fun clearToken() = withContext(Dispatchers.IO) {
        context.dataStore.edit { it.remove(PreferencesKeys.TOKEN) }
    }

    private object PreferencesKeys {
        val TOKEN = stringPreferencesKey("access_token")
    }

    companion object {
        @Volatile
        private var INSTANCE: TokenDataStore? = null

        fun getInstance(context: Context): TokenDataStore {
            return INSTANCE ?: synchronized(this) {
                // 使用applicationContext避免持有具体组件实例
                val instance = TokenDataStore(context.applicationContext)
                INSTANCE = instance
                instance
            }
        }
    }
}

然后在需要使用的地方获取实例:

// 在Activity/Fragment中
val dataStoreManager = TokenDataStore.getInstance(this)
// 在Application的onCreate中提前初始化(可选)
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        TokenDataStore.getInstance(this)
    }
}

为什么原代码会触发警告?

原代码中MyApplication伴生对象的dataStore变量是静态的,它持有了MyApplication的实例引用。静态变量的生命周期和应用进程一致,当Application因配置变更(如屏幕旋转)被销毁重建时,旧的Application实例会被这个静态引用卡住,无法被GC回收,最终导致内存泄漏。

内容的提问来源于stack exchange,提问作者nero-123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:03:29