英文:
Android sharedPreferences.commit unable to read file
问题
以下是翻译好的内容:
我有一个广播接收器,它清除了共享首选项文件,然后进行提交。我的应用程序因为清除操作成功执行了,但由于某种原因提交操作无法读取该文件,导致应用程序出现了 ANR(应用程序无响应)。
广播接收器也超时了。
我无法理解堆栈跟踪的含义。有人可以帮助我理解发生了什么吗?是否有方法可以避免这种情况?
以下是堆栈跟踪:
"main" 优先级=5 线程编号=1 本机
| 线程组="main" sCount=1 dsCount=0 obj=0x7599a000 自身=0xb84a5e98
| sysTid=15259 优先级=0 cgrp=bg_non_interactive 调度=0/0 句柄=0xb6f1d000
| 状态=S schedstat=( 137176743 6798788178 493 ) utm=4 stm=9 核心=0 HZ=100
| 栈=0xbe0d0000-0xbe0d2000 栈大小=8MB
| 持有的互斥锁=
kernel: (无法读取 /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.access$900(SharedPreferencesImpl.java:53)
at android.app.SharedPreferencesImpl$2.run(SharedPreferencesImpl.java:532)
- locked <@addr=0x22d600f0> (a java.lang.Object)
at android.app.SharedPreferencesImpl.enqueueDiskWrite(SharedPreferencesImpl.java:551)
at android.app.SharedPreferencesImpl.access$100(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();
}
}
英文:
I have a Broadcast Receiver that clears the shared preferences file and then commits. My app runs into ANR since the clear works but the commit is not able to read the file for some reason
The broadcast receiver also times out..
I am unable to understand what the stack trace means. Can someone help me understand what happened here? And is there a way to avoid this?
Here is the stack trace :
"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.access$900(SharedPreferencesImpl.java:53)
at android.app.SharedPreferencesImpl$2.run(SharedPreferencesImpl.java:532)
- locked <@addr=0x22d600f0> (a java.lang.Object)
at android.app.SharedPreferencesImpl.enqueueDiskWrite(SharedPreferencesImpl.java:551)
at android.app.SharedPreferencesImpl.access$100(SharedPreferencesImpl.java:53)
at android.app.SharedPreferencesImpl$EditorImpl.commit(SharedPreferencesImpl.java:473)
Here is the sample code :
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();
}
}
答案1
得分: 1
发生的情况是你在主线程上加载了一个文件,这花费的时间过长,导致应用出现了ANR。
为什么会这样?
你的代码中有两个问题可能导致ANR。
当你第一次调用context.getSharedPreferences()
时,Android会启动一个任务来读取底层的偏好设置文件(一个XML文件),并将所有数据加载到内存中。在这个加载过程中,对你所拥有的SharedPreferences
对象获取任何数据的所有调用都会被阻塞,直到加载任务完成。这包括你在onReceive()
中进行的对edit()
的调用。
第二个问题在于对commit()
的调用。这会在你的SharedPreferences
中执行同步写入底层偏好设置文件的操作,这会阻塞调用线程(在你的情况下,又是主线程)。这也会导致ANR。
可能的解决方案
首先,我建议将加载偏好设置的调用移到应用启动的较早阶段。一个好的位置是Application.onCreate()
。在这个方法中,你只应该调用getSharedPreferences()
ONLY。你不希望在这里阻塞主线程。这样会在onReceive()
被调用之前给予偏好设置足够的时间来加载。
其次,不要使用commit()
。我建议改用apply()
。这会确保数据立即存储在内存中,并异步地将数据写入底层的偏好设置文件。然而,仍然有可能出现ANR,特别是因为Android在接收器完成onReceive()
时(以及其他情况下),将用于执行写入任务的队列异步刷新到主线程。我不会过于担心这种情况,只是提醒你要注意这一点。
最后,我不建议使用grandcentrix/tray,因为它已不再受支持,如果使用不当可能会导致其他ANR问题。如果你的应用是单进程应用,请不要使用多进程的解决方案。
英文:
What is happening here is that you are loading a file on the main thread, which is taking too long and causing the app to ANR.
Why?
There are two problems with what you have in your code that could lead to an ANR.
When you call context.getSharedPreferences()
for the first time, Android launches a job that reads the underlying preference file (an XML file) and loads all the data into memory. While this load is occurring, any and all calls to get any data from the SharedPreferences
object you have will block until the load job is completed. This includes calls to edit()
which you are doing in your onReceive()
.
The second problem is with the call to commit()
. This performs a synchronous write to the underlying preference file in your SharedPreferences
, which blocks the calling thread (in your case, this is again the Main thread). This will also lead to ANRs.
Possible Solution
Firstly, I would move the call to load your preferences closer to the start of the app start. One good place is Application.onCreate()
. In this method, you should only call getSharedPreferences()
ONLY. You don't want to block the main thread here at all. This will give your preferences time to load before onReceive()
is called.
Secondly, don't use commit()
. I would recommend using apply()
instead. This ensures the data is stored in memory immediately, while writing the data asynchronously to the underlying preference file. There is still a chance for an ANR however, especially because the queue that Android uses to do the write jobs asynchronously flushes onto the main thread when a receiver completes onReceive()
(as well as in other cases). I wouldn't worry about this situation all too much, just something to be aware of.
Finally, I would not recommend using grandcentrix/tray, as it is no longer supported, and could cause other ANRs if used improperly. If your app is a single process app, don't use a multi-process solution.
答案2
得分: 0
每个接收器都会在一个新进程中启动,但是在不同进程之间不支持使用SharedPreferences。
因此,如果你的应用程序的API级别小于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包装器。
英文:
Every receiver get always started in a new proces, but SharedPreferences is not supported between different processes.
So if your app < API Level 23
, you can use the flag Context.MODE_MULTI_PROCESS
:
context.getSharedPreferences("mypreferences", Context.MODE_PRIVATE |Context.MODE_MULTI_PROCESS);
NOTE: Since API Level 23 is the flag Context.MODE_MULTI_PROCESS
deprecated and you should use ContentProvider to share the properties between processes.
This library https://github.com/grandcentrix/tray can be helpfull.
Basically it's a ContentProvider wrapper with some additional features.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论