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。如果您的应用是单进程应用,请不要使用多进程解决方案。
我有一个广播接收器清除共享首选项文件然后提交。我的应用程序遇到 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。如果您的应用是单进程应用,请不要使用多进程解决方案。