创建SharedPreference时触发StrictModeDiskReadViolation问题求助
我之前也踩过这个坑,咱们来捋清楚问题根源和解决办法:
问题原因
StrictMode的磁盘读违规,本质是因为在主线程执行了磁盘IO操作。你的Dagger组件是在Application的onCreate()里初始化的,而provideSharedPreferences方法调用PreferenceManager.getDefaultSharedPreferences(context)时,会直接去读取磁盘上的SharedPreferences文件——这就刚好撞在了StrictMode的检测枪口上。
可行解决方案
1. 用Dagger的Lazy延迟初始化(最推荐)
把SharedPreferences的实例化延迟到第一次实际使用的时候,而不是Dagger组件初始化阶段。这样只要你第一次调用它的地方不在主线程,就不会触发违规。
修改你的provide方法:
@Provides @Singleton @JvmStatic fun provideSharedPreferences(@AppContext context: Context): Lazy<SharedPreferences> = Lazy { PreferenceManager.getDefaultSharedPreferences(context) }
然后在需要使用的地方注入Lazy<SharedPreferences>,当你需要用的时候调用.get()即可:
class SomeRepository @Inject constructor( private val prefsLazy: Lazy<SharedPreferences> ) { fun doSomething() { // 如果这里是在后台线程调用,就完全没问题 val prefs = prefsLazy.get() // ...后续操作 } }
2. 用Provider控制初始化时机
和Lazy逻辑类似,Provider适合你需要手动控制初始化时机的场景,比如在后台线程主动触发实例化:
修改provide方法:
@Provides @Singleton @JvmStatic fun provideSharedPreferences(@AppContext context: Context): Provider<SharedPreferences> = Provider { PreferenceManager.getDefaultSharedPreferences(context) }
然后在后台线程触发初始化:
class SomeInitializer @Inject constructor( private val prefsProvider: Provider<SharedPreferences> ) { fun initInBackground() { CoroutineScope(Dispatchers.IO).launch { // 在这里触发初始化,磁盘读在IO线程执行 prefsProvider.get() } } }
3. 后台线程初始化Dagger组件(适合必须提前加载的场景)
如果你的业务逻辑要求SharedPreferences必须在App启动时就准备好,可以把Dagger组件的初始化移到后台线程,同时在后台完成SharedPreferences的实例化:
在Application里:
private lateinit var appComponent: AppComponent override fun onCreate() { super.onCreate() // 开IO线程初始化 CoroutineScope(Dispatchers.IO).launch { val prefs = PreferenceManager.getDefaultSharedPreferences(this@MyApp) // 初始化组件并传入预加载好的prefs appComponent = DaggerAppComponent.factory().create(this@MyApp, prefs) // 如果需要通知主线程初始化完成,可以用withContext(Dispatchers.Main) } } // 提供组件获取方法 fun getAppComponent(): AppComponent = appComponent
然后修改Dagger模块,直接注入已初始化的SharedPreferences:
@Module(...) abstract class AppModule { @Module companion object { @Provides @Singleton @JvmStatic fun provideSharedPreferences(prefs: SharedPreferences): SharedPreferences = prefs } @Binds @AppContext @Singleton abstract fun provideAppContext(application: Application): Context }
注意:这种方式要处理好组件未初始化完成时的空指针问题,比如给业务层加初始化完成的回调。
4. 临时放宽StrictMode(权宜之计,不推荐长期用)
如果你只是在调试阶段想快速绕过这个警告,可以临时在onCreate()开头放宽StrictMode的磁盘读限制:
override fun onCreate() { if (BuildConfig.DEBUG) { // 保存原策略,方便后续恢复 val originalPolicy = StrictMode.getThreadPolicy() StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder(originalPolicy) .permitDiskReads() .build()) } // 初始化Dagger组件... if (BuildConfig.DEBUG) { // 恢复原StrictMode策略 StrictMode.setThreadPolicy(originalPolicy) } }
这个方法能快速消警告,但会掩盖其他主线程磁盘读的问题,只适合临时调试用。
总结
优先推荐用Lazy的方式,改动最小,也符合Dagger的设计理念。如果业务有特殊要求,再考虑后台初始化组件的方案。尽量不要依赖放宽StrictMode的方式,毕竟StrictMode的存在就是帮我们提前发现主线程阻塞的问题。
内容的提问来源于stack exchange,提问作者azizbekian

