Kotlin开发SharedPreference初始化后部分用户触发未初始化异常崩溃
问题原因
- ContentProvider提前访问:Android系统中,ContentProvider的初始化时机早于Application的
onCreate回调。如果你的项目中集成的第三方SDK、或者自定义的ContentProvider在初始化阶段就访问了PreferenceUtil的属性,此时init方法还未执行,就会触发未初始化异常。 - 并发访问竞态条件:
init方法中的isInitialized判断和mPref赋值不是原子操作,也没有加同步锁。如果在应用启动阶段有多个线程同时触发init调用,或者在init执行完成前就有其他线程访问mPref相关属性,就会出现判断已经通过但赋值还未完成的情况,触发异常。 - 特殊进程/ROM异常:如果应用配置了多进程,部分特殊进程的初始化流程被厂商ROM篡改,或者你做了进程区分的初始化逻辑跳过了PreferenceUtil的初始化,也会出现该问题。
解决方法
方案1:懒加载初始化(最稳妥,可省略手动init步骤)
直接将mPref修改为懒加载模式,自动获取ApplicationContext,完全避免手动初始化的时序问题:
// 先在Application中提供全局ApplicationContext入口 class MyApplication : Application() { companion object { lateinit var appContext: Context private set } override fun onCreate() { super.onCreate() appContext = this.applicationContext } } // PreferenceUtil修改为自动初始化 object PreferenceUtil { private val mPref: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(MyApplication.appContext) } // 其余属性读写逻辑不变 var launchCount: Int get() = mPref.getInt("launch_count", 0) set(value) = mPref.edit().putInt("launch_count", value).apply() }
方案2:修复原有init逻辑的线程安全问题
如果要保留原有的手动init设计,修改init方法为线程安全,同时排查所有ContentProvider逻辑,禁止在ContentProvider中访问PreferenceUtil:
object PreferenceUtil { private lateinit var mPref: SharedPreferences @Synchronized // 加同步锁保证原子性 fun init(context: Context) { if (!this::mPref.isInitialized) { mPref = PreferenceManager.getDefaultSharedPreferences(context.applicationContext) } } }
方案3:访问时兜底初始化
在所有读写mPref的属性中加入初始化检查,发现未初始化时主动触发初始化,作为兜底方案:
var launchCount: Int get() { if (!this::mPref.isInitialized) { init(MyApplication.appContext) } return mPref.getInt("launch_count", 0) } set(value) { if (!this::mPref.isInitialized) { init(MyApplication.appContext) } mPref.edit().putInt("launch_count", value).apply() }
内容的提问来源于stack exchange,提问作者Jazib Khan
相关产品推荐
相关产品推荐

