StrictMode: StrictModeDiskReadViolation при создании SharedPreference

У меня есть проект с настройкой кинжала со следующим методом провайдера:

@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

}

А вот код из приложения 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()
}

Запуск проекта на эмуляторе API 27 приводит к следующему поведению:

Со следующими журналами:

D/StrictMode: нарушение политики StrictMode; ~ продолжительность = 275 мс: android.os.StrictMode $ StrictModeDiskReadViolation: policy = 196671 нарушение = 2 в android.os.StrictMode $ AndroidBlockGuardPolicy.onReadFromDisk (StrictMode.java: 1440) в java.io.UnixFileSystem.checkAccess (UnixFileSystem.java: 251) в java.io.File.exists(File.java:807) в android.app.ContextImpl.getDataDir(ContextImpl.java:2197) в android.app.ContextImpl.getPreferencesDir(ContextImpl.java:517) в android. app.ContextImpl.getSharedPreferencesPath(ContextImpl.java:714) в android.app.ContextImpl.getSharedPreferences(ContextImpl.java:368) в android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:167) в android.preference.PreferenceManager.getDefaultSharedPreferences (PreferenceManager.java:526) в com.some.package.di.module.AppModule$Companion.provideSharedPreferences(AppModule.kt:112)...

Это означает, что следующее res.exists() читает с диска:

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());
}

И поскольку это происходит в потоке пользовательского интерфейса - StrictModeDiskReadViolation результатов.

Afaik, не существует API для исключения некоторого фрагмента кода (например, по имени пакета) из конфигурации StrictMode. Практически, я могу оставить SharedPreferences связанные вещи для чтения с диска в потоке пользовательского интерфейса.

Я не хочу отключать правило чтения/записи StrictMode из-за этой проблемы.

Вопрос

Каков правильный способ изящного восстановления после этого сценария?


person azizbekian    schedule 15.04.2018    source источник


Ответы (1)


Afaik, не существует API для исключения некоторого фрагмента кода (например, по имени пакета) из конфигурации StrictMode. Практически я могу оставить материалы, связанные с SharedPreferences, для чтения с диска в потоке пользовательского интерфейса.

Он существует. StrictMode.allowThreadDiskReads() изменяет разрешения, чтобы разрешить чтение, и возвращает старое ThreadPolicy, чтобы его можно было сбросить после завершения чтения. Затем это можно использовать вместе с настройкой «try-finally», чтобы разрешить чтение для одного действия.

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

Вы можете создать функцию kotlin, которая обрабатывает восстановление с помощью лямбды:

fun <T> allowReads(block: () -> T): T {
    val oldPolicy = StrictMode.allowThreadDiskReads()
    try {
        return block()
    } finally {
        StrictMode.setThreadPolicy(oldPolicy)
    }
}
person Kiskae    schedule 15.04.2018
comment
Господа, это правильный ответ. Спасибо за просвещение. Для других читателей, помимо StrictMode.allowThreadDiskReads(), мне пришлось выполнить еще и StrictMode.allowThreadDiskWrites(). - person azizbekian; 15.04.2018