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

Android应用启动时Activity重复创建引发DataStore多实例异常

解决DataStore多实例异常及相关疑问解答

问题背景

开发使用DataStore<Preferences>存储数据的项目时,卸载重装应用会随机出现异常:

java.lang.IllegalStateException: There are multiple DataStores active for the same file

结合日志对比正常与崩溃流程,存在两个核心疑问,以下是针对性解答和解决方案。


疑问解答

1. 无屏幕旋转时Activity为何会销毁重建?

这是Android系统在冷启动/重装后启动时的偶发行为:

  • 系统在应用首次启动时,可能因资源调度、进程优先级调整,或者ADB/Studio安装后的启动流程异常,触发Activity的销毁与重建。
  • 这种情况属于系统层面的边缘场景,在频繁卸载重装时更容易触发,和屏幕旋转这类配置变更无关。

2. 新Activity创建时,单例的instance为何被重置为null?

你的AppControllerSettings单例依赖companion object中的instance字段,而该字段的生命周期和类本身绑定:

  • 当AppControllerSettings的唯一引用仅存在于已销毁的MainActivity实例中,Activity被GC回收后,AppControllerSettings类会因为没有存活实例和静态强引用,被触发类卸载,导致instance字段重置为null。
  • 新Activity调用getInstance时,会创建新的AppControllerSettings实例,进而初始化新的DataStore,最终触发多实例冲突异常。

解决方案

核心思路

让单例持有不会被轻易回收的强引用(如Application实例),确保AppControllerSettings类不会被卸载,从而避免重复创建DataStore。

步骤1:自定义Application并初始化单例

创建自定义Application类,在onCreate中提前初始化单例,利用Application的进程级生命周期确保单例存活:

class MyApp : Application() {
    companion object {
        lateinit var instance: MyApp
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        // 提前初始化单例,避免后续类卸载
        runBlocking {
            AppControllerSettings.getInstance()
        }
    }
}

在AndroidManifest.xml中声明该Application:

<application
    android:name=".MyApp"
    ...>
    <!-- 其他配置 -->
</application>

步骤2:优化单例实现(推荐)

修改AppControllerSettings,使用lazy初始化单例,同时改用Application Context避免内存泄漏:

class AppControllerSettings private constructor(context: Context) {
    companion object {
        private val USERS_KEY = stringSetPreferencesKey("users")
        
        // lazy初始化确保全局唯一,类加载器持有强引用
        private val instance by lazy {
            AppControllerSettings(MyApp.instance).apply {
                runBlocking(Dispatchers.IO) {
                    try {
                        val preferences = preferencesStore.data.first()
                        preferences[USERS_KEY]?.let { users.addAll(it) }
                    } catch (ioe: IOException) {
                        debugError("failed to read app controller settings from preferences store", ioe)
                    }
                }
            }
        }

        fun getInstance(): AppControllerSettings = instance
    }

    // 使用Application Context,避免持有Activity引用
    private val preferencesStore = createPreferencesStoreFromFile(MyApp.instance, "app_controller_settings")
    private val users: MutableSet<String> = mutableSetOf()

    suspend fun addUserToSignInHistory(user: User): Result<Unit> {
        return runCatching {
            withContext(Dispatchers.IO) {
                preferencesStore.edit { preferences ->
                    users.add(user.id)
                    preferences[USERS_KEY] = users
                }
            }
        }
    }

    fun hasUserPreviouslySignedIn(user: User): Boolean {
        return users.contains(user.id)
    }
}

步骤3:修改Activity中的调用方式

移除lifecycleScope中的异步初始化,直接调用getInstance():

class MainActivity : ComponentActivity() {
    private val appControllerSettings = AppControllerSettings.getInstance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 后续逻辑
    }
}

总结

问题的核心是单例类被类卸载导致重复创建DataStore。通过在Application中提前初始化单例,确保类不会被卸载,同时使用Application Context避免内存泄漏,即可彻底解决多DataStore实例的异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 04:05:44