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

部分Android设备出现GameStorage单例prefs未初始化崩溃问题求助

问题分析
  • GameStorage继承Activity是致命错误:Activity是Android系统管理的组件,不能通过new GameStorage()手动实例化,这种方式创建的Activity未经过系统生命周期初始化,上下文完全无效,还会引发未知异常。
  • 初始化时机不可靠:仅在MainActivity中执行初始化,如果用户通过其他入口(如通知、DeepLink、进程重启后直接从最近任务打开GameActivity)启动应用,会跳过MainActivity,导致init()未被调用,直接访问prefs就会触发lateinit未初始化崩溃。
  • 缺少初始化校验:lateinit变量未做初始化检查,一旦未调用init()就访问,必然触发崩溃。
解决方案

1. 重构GameStorage,移除Activity继承

Activity组件绝不能作为单例使用,改成普通类实现单例逻辑:

class GameStorage private constructor() {

    private object HOLDER {
        val SHARED = GameStorage()
    }

    companion object {
        val shared: GameStorage by lazy { HOLDER.SHARED }
    }

    private var prefs: SharedPreferences? = null

    private val prefsName = "GameStorage"

    fun init(context: Context) {
        // 使用ApplicationContext避免内存泄漏
        prefs = context.applicationContext.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
    }

    // 增加初始化检查,避免未初始化时崩溃
    private fun checkInit() {
        checkNotNull(prefs) { "GameStorage must be initialized with init() before use" }
    }

    private fun read(key: String, defaultValue: Boolean): Boolean {
        checkInit()
        return prefs!!.getBoolean(key, defaultValue)
    }

    fun write(key: String, value: Boolean) {
        checkInit()
        prefs!!.edit().putBoolean(key, value).apply()
        // 建议用apply()代替commit(),apply是异步提交,性能更优
    }

    var isSoundOn: Boolean
        get() = read("isSoundOn", true)
        set(value) = write("isSoundOn", value)
}

2. 提前初始化时机,确保全局触发

在自定义Application的onCreate()中执行初始化,这样所有组件启动前都会完成初始化,避免依赖某个Activity:

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        GameStorage.shared.init(this)
    }
}

记得在AndroidManifest.xml中配置这个Application:

<application
    android:name=".MyApp"
    ...>
    ...
</application>

3. 可选:增加兜底初始化逻辑

如果担心极端场景下初始化未触发,可以在访问prefs时自动初始化(需要全局ApplicationContext支持):

// 修改GameStorage中的checkInit方法,自动完成初始化
private fun checkInit() {
    if (prefs == null) {
        // 通过自定义Application获取全局上下文
        val context = MyApp.getInstance()
        prefs = context.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
    }
}

// 在MyApp中添加获取实例的方法
class MyApp : Application() {
    companion object {
        private lateinit var instance: MyApp
        fun getInstance() = instance
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        GameStorage.shared.init(this)
    }
}
额外注意点
  • 始终使用ApplicationContext持有SharedPreferences,避免持有Activity上下文导致内存泄漏。
  • 提交SharedPreferences修改时,优先使用apply()而非commit(),commit()是同步操作,可能阻塞主线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:37:43