部分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
相关产品推荐
相关产品推荐

