Android sharedPreferences.commit 无法读取文件

Android sharedPreferences.commit unable to read file

我有一个广播接收器清除共享首选项文件然后提交。我的应用程序遇到 ANR,因为 clear 可以正常工作,但提交由于某种原因无法读取文件

广播接收者也超时..

我无法理解堆栈跟踪的含义。有人可以帮我了解这里发生了什么吗?有没有办法避免这种情况?

这是堆栈跟踪:

"main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 obj=0x7599a000 self=0xb84a5e98
  | sysTid=15259 nice=0 cgrp=bg_non_interactive sched=0/0 handle=0xb6f1d000
  | state=S schedstat=( 137176743 6798788178 493 ) utm=4 stm=9 core=0 HZ=100
  | stack=0xbe0d0000-0xbe0d2000 stackSize=8MB
  | held mutexes=
  kernel: (couldn't read /proc/self/task/15259/stack)
  native: #00 pc 00012ab0  /system/lib/libc.so (syscall+28)
  native: #01 pc 000a98af  /system/lib/libart.so (_ZN3art17ConditionVariable4WaitEPNS_6ThreadE+82)
  native: #02 pc 001c1529  /system/lib/libart.so (_ZN3art3JNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh+672)
  native: #03 pc 000139eb  /system/lib/libjavacore.so (???)
  native: #04 pc 00020dfd  /system/lib/libjavacore.so (???)
  native: #05 pc 002859e3  /system/framework/arm/boot.oat (Java_libcore_io_Posix_open__Ljava_lang_String_2II+118)
  at libcore.io.Posix.open(Native method)
  at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
  at libcore.io.IoBridge.open(IoBridge.java:442)
  at java.io.FileOutputStream.<init>(FileOutputStream.java:89)
  at java.io.FileOutputStream.<init>(FileOutputStream.java:74)
  at android.app.SharedPreferencesImpl.createFileOutputStream(SharedPreferencesImpl.java:578)
  at android.app.SharedPreferencesImpl.writeToFile(SharedPreferencesImpl.java:631)
  at android.app.SharedPreferencesImpl.access0(SharedPreferencesImpl.java:53)
  at android.app.SharedPreferencesImpl.run(SharedPreferencesImpl.java:532)
  - locked <@addr=0x22d600f0> (a java.lang.Object)
  at android.app.SharedPreferencesImpl.enqueueDiskWrite(SharedPreferencesImpl.java:551)
  at android.app.SharedPreferencesImpl.access0(SharedPreferencesImpl.java:53)
  at android.app.SharedPreferencesImpl$EditorImpl.commit(SharedPreferencesImpl.java:473)

示例代码如下:

public class AppBroadcastReceiver extends BroadcastReceiver {

   @Override
    public void onReceive(Context context, Intent intent) {

       SharedPreferences mSharedPre = context.getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
       SharedPreferences.Editor sharedPrefEditor = mSharedPre.edit();
       sharedPrefEditor.clear();

       sharedPrefEditor.commit();

    }

}

每个接收者总是在一个新进程中启动,但是不同进程之间不支持SharedPreferences。 所以如果your app < API Level 23,你可以使用标志Context.MODE_MULTI_PROCESS:

context.getSharedPreferences("mypreferences", Context.MODE_PRIVATE |Context.MODE_MULTI_PROCESS);

注意:由于 API 级别 23 是已弃用的标志 Context.MODE_MULTI_PROCESS,您应该使用 ContentProvider 在进程之间共享属性。 这个库 https://github.com/grandcentrix/tray 很有用。 基本上它是一个具有一些附加功能的 ContentProvider 包装器。

这里发生的事情是您正在主线程上加载文件,这花费的时间太长并导致应用出现 ANR。

为什么?

您的代码中存在两个可能导致 ANR 的问题。

当您第一次调用 context.getSharedPreferences() 时,Android 会启动一个作业来读取底层首选项文件(XML 文件)并将所有数据加载到内存中。发生此加载时,任何和所有从您拥有的 SharedPreferences 对象获取任何数据的调用都将阻塞,直到加载作业完成。这包括您在 onReceive().

中对 edit() 的调用

第二个问题是对 commit() 的调用。这会对您的 SharedPreferences 中的基础首选项文件执行同步写入,这会阻止调用线程(在您的情况下,这又是主线程)。这也会导致 ANR。

可能的解决方案

首先,我会将加载您的首选项的调用移到更靠近应用程序启动的位置。一个好的地方是 Application.onCreate()。在此方法中,您应该只调用 getSharedPreferences() ONLY。你根本不想在这里阻塞主线程。这将使您的偏好有时间在调用 onReceive() 之前加载。

其次,不要使用commit()。我建议改用 apply() 。这确保数据立即存储在内存中,同时将数据异步写入底层首选项文件。但是仍然有可能发生 ANR,特别是因为当接收者完成 onReceive()(以及其他情况)时,Android 用于执行写入作业的队列异步刷新到主线程。我不会太担心这种情况,只是需要注意一些事情。

最后,我不推荐使用grandcentrix/tray,因为它不再受支持,如果使用不当可能会导致其他ANR。如果您的应用是单进程应用,请不要使用多进程解决方案。