StrictMode: StrictModeDiskReadViolation when creating SharedPreference
Asked Answered
D

1

10

I have a project with dagger setup with following provider method:

@Module(...)
abstract class AppModule {

  @Module
  companion object {
    ...
    @Provides
    @Singleton
    @JvmStatic
    fun provideSharedPreferences(@AppContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
  }

  @Binds
  @AppContext
  @Singleton
  abstract fun provideAppContext(application: Application): Context

}

And here's a code from application's onCreate():

override fun onCreate() {
  if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
        .detectAll()
        .penaltyLog()
        .penaltyDialog()
        .build())

    StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
        .detectAll()
        .penaltyLog()
        .build())

    Timber.plant(Timber.DebugTree())
  }
  ...
  super.onCreate()
}

Running the project on API 27 emulator results in following behavior:

With following logs:

D/StrictMode: StrictMode policy violation; ~duration=275 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=196671 violation=2 at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1440) at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:251) at java.io.File.exists(File.java:807) at android.app.ContextImpl.getDataDir(ContextImpl.java:2197) at android.app.ContextImpl.getPreferencesDir(ContextImpl.java:517) at android.app.ContextImpl.getSharedPreferencesPath(ContextImpl.java:714) at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:368) at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:167) at android.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:526) at com.some.package.di.module.AppModule$Companion.provideSharedPreferences(AppModule.kt:112) ...

What this means, is that following res.exists() reads from disk:

if (!res.exists() && android.os.Process.myUid() == android.os.Process.SYSTEM_UID) {
    Log.wtf(TAG, "Data directory doesn't exist for package " + getPackageName(),
            new Throwable());
}

And because that's happening on UI thread - StrictModeDiskReadViolation results.

Afaik, there does not exist an API to exclude some chunk of code (e.g. by package name) from StrictMode configuration. Practically, I'm ok to leave SharedPreferences related stuff to read from disk on UI thread.

I do not want to turn off read/write StrictMode rule because of this problem.

Question

What is the correct way to gracefully recover from this scenario?

Delegation answered 15/4, 2018 at 12:14 Comment(0)
D
23

Afaik, there does not exist an API to exclude some chunk of code (e.g. by package name) from StrictMode configuration. Practically, I'm ok to leave SharedPreferences related stuff to read from disk on UI thread.

It does exist. StrictMode.allowThreadDiskReads() changes the permissions to allow for reads and returns the old ThreadPolicy so it can be reset once the read is done. This can then be used together with a "try-finally" setup to allow the read for a single action.

val oldPolicy = StrictMode.allowThreadDiskReads()
try {
    // Do reads here
} finally {
    StrictMode.setThreadPolicy(oldPolicy)
}

You could create a kotlin function that handles the restoration with a lambda:

fun <T> allowReads(block: () -> T): T {
    val oldPolicy = StrictMode.allowThreadDiskReads()
    try {
        return block()
    } finally {
        StrictMode.setThreadPolicy(oldPolicy)
    }
}
Doscher answered 15/4, 2018 at 15:29 Comment(1)
Sir, this is the correct answer. Thanks for enlightening. For other readers, in addition to StrictMode.allowThreadDiskReads() I had to perform StrictMode.allowThreadDiskWrites() also.Delegation

© 2022 - 2024 — McMap. All rights reserved.