Android SharedPreferences.onSharedPreferenceChangeListener 没有被一致调用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2542938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
SharedPreferences.onSharedPreferenceChangeListener not being called consistently
提问by synic
I'm registering a preference change listener like this (in the onCreate()
of my main activity):
我正在注册一个这样的偏好更改监听器(在onCreate()
我的主要活动中):
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(
SharedPreferences prefs, String key) {
System.out.println(key);
}
});
The trouble is, the listener is not always called. It works for the first few times a preference is changed, and then it is no longer called until I uninstall and reinstall the app. No amount of restarting the application seems to fix it.
问题是,侦听器并不总是被调用。它适用于首选项更改的前几次,然后在我卸载并重新安装应用程序之前不再调用它。重新启动应用程序似乎无法修复它。
I found a mailing list threadreporting the same problem, but no one really answered him. What am I doing wrong?
我发现一个邮件列表线程报告了同样的问题,但没有人真正回答他。我究竟做错了什么?
回答by Blanka
This is a sneaky one. SharedPreferences keeps listeners in a WeakHashMap. This means that you cannot use an anonymous inner class as a listener, as it will become the target of garbage collection as soon as you leave the current scope. It will work at first, but eventually, will get garbage collected, removed from the WeakHashMap and stop working.
这是一个偷偷摸摸的。SharedPreferences 将侦听器保存在 WeakHashMap 中。这意味着您不能使用匿名内部类作为侦听器,因为一旦离开当前作用域,它就会成为垃圾回收的目标。一开始它会工作,但最终会被垃圾收集,从 WeakHashMap 中删除并停止工作。
Keep a reference to the listener in a field of your class and you will be OK, provided your class instance is not destroyed.
在您的类的字段中保留对侦听器的引用,只要您的类实例没有被破坏,您就可以了。
i.e. instead of:
即而不是:
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Implementation
}
});
do this:
做这个:
// Use instance field for listener
// It will not be gc'd as long as this instance is kept referenced
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Implementation
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
The reason unregistering in the onDestroy method fixes the problem is because to do that you had to save the listener in a field, therefore preventing the issue. It's the saving the listener in a field that fixes the problem, not the unregistering in onDestroy.
在 onDestroy 方法中取消注册解决问题的原因是因为这样做您必须将侦听器保存在一个字段中,从而防止出现问题。是将侦听器保存在解决问题的字段中,而不是在 onDestroy 中取消注册。
UPDATE: The Android docs have been updatedwith warningsabout this behavior. So, oddball behavior remains. But now it's documented.
回答by Samuel
this accepted answer is ok, as for me it is creating new instanceeach time the activity resumes
这个接受的答案是可以的,对我来说,每次活动恢复时它都会创建新实例
so how about keeping the reference to the listener within the activity
那么如何在活动中保留对侦听器的引用
OnSharedPreferenceChangeListener myPrefListner = new OnSharedPreferenceChangeListener(){
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// your stuff
}
};
and in your onResume and onPause
并在您的 onResume 和 onPause
@Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(myPrefListner);
}
@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(myPrefListner);
}
this will very similar to what you are doing except we are maintaining a hard reference.
这将与您正在做的非常相似,除非我们保持硬参考。
回答by Bim
As this is the most detailed page for the topic I want to add my 50ct.
由于这是该主题最详细的页面,我想添加我的 50ct。
I had the problem that OnSharedPreferenceChangeListener wasn't called. My SharedPreferences are retrieved at the start of the main Activity by:
我遇到了未调用 OnSharedPreferenceChangeListener 的问题。我的 SharedPreferences 在主活动开始时通过以下方式检索:
prefs = PreferenceManager.getDefaultSharedPreferences(this);
My PreferenceActivity code is short and does nothing except showing the preferences:
我的 PreferenceActivity 代码很短,除了显示首选项之外什么都不做:
public class Preferences extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// load the XML preferences file
addPreferencesFromResource(R.xml.preferences);
}
}
Every time the menu button is pressed I create the PreferenceActivity from the main Activity:
每次按下菜单按钮时,我都会从主活动创建 PreferenceActivity:
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
//start Preference activity to show preferences on screen
startActivity(new Intent(this, Preferences.class));
//hook into sharedPreferences. THIS NEEDS TO BE DONE AFTER CREATING THE ACTIVITY!!!
prefs.registerOnSharedPreferenceChangeListener(this);
return false;
}
Notethat registering the OnSharedPreferenceChangeListener needs to be done AFTER creating the PreferenceActivity in this case, else the Handler in the main Activity won't be called!!! It took me some sweet time to realize that...
注意在这种情况下需要在创建PreferenceActivity之后注册OnSharedPreferenceChangeListener,否则主Activity中的Handler不会被调用!!!我花了一些甜蜜的时间才意识到......
回答by PrashanD
The accepted answer creates a SharedPreferenceChangeListener
every time onResume
is called. @Samuelsolves it by making SharedPreferenceListener
a member of the Activity class. But there's a third and a more straightforward solution that Googlealso uses in this codelab. Make your activity class implement the OnSharedPreferenceChangeListener
interface and override onSharedPreferenceChanged
in the Activity, effectively making the Activity itself a SharedPreferenceListener
.
接受的答案会在SharedPreferenceChangeListener
每次onResume
调用时创建一个。@Samuel通过成为SharedPreferenceListener
Activity 类的成员来解决它。但是,Google在此 codelab 中还使用了第三种更直接的解决方案。让你的活动类实现OnSharedPreferenceChangeListener
接口并onSharedPreferenceChanged
在活动中覆盖,有效地使活动本身成为一个SharedPreferenceListener
.
public class MainActivity extends Activity implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
}
@Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onStop() {
super.onStop();
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
}
回答by Hamed Jaliliani
Kotlin Code for register SharedPreferenceChangeListener it detect when change will happening on the saved key :
用于注册 SharedPreferenceChangeListener 的 Kotlin 代码,它检测保存的密钥何时发生更改:
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener { sharedPreferences, key ->
if(key=="language") {
//Do Something
}
}
you can put this code in onStart() , or somewhere else.. *Consider that you must use
您可以将此代码放在 onStart() 或其他地方.. *考虑您必须使用
if(key=="YourKey")
or your codes inside "//Do Something " block will be run wrongly for every change that will happening in any other key in sharedPreferences
或者您在“//Do something”块中的代码将在 sharedPreferences 中的任何其他键中发生的每个更改都错误地运行
回答by Asghar Musani
So, I don't know if this would really help anyone though, it solved my issue.
Even though I had implemented the OnSharedPreferenceChangeListener
as stated by the accepted answer. Still, I had an inconsistency with the listener being called.
所以,我不知道这是否真的对任何人有帮助,但它解决了我的问题。即使我已经OnSharedPreferenceChangeListener
按照公认的答案执行了。尽管如此,我还是与被调用的听众不一致。
I came here to understand that the Android just sends it for garbage collection after some time. So, I looked over at my code.
To my shame, I had not declared the listener GLOBALLYbut instead inside the onCreateView
. And that was because I listened to the Android Studio telling me to convert the listener to a local variable.
我来到这里是为了理解 Android 只是在一段时间后将其发送以进行垃圾收集。所以,我查看了我的代码。令我遗憾的是,我没有在GLOBALLY 中声明侦听器,而是在onCreateView
. 那是因为我听 Android Studio 告诉我将侦听器转换为局部变量。
回答by androidyue
It make sense that the listeners are kept in WeakHashMap.Because most of the time, developers prefer to writing the code like this.
监听器保存在 WeakHashMap 中是有道理的。因为大多数时候,开发人员更喜欢这样编写代码。
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener(
new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key);
}
});
This may seem not bad. But if the OnSharedPreferenceChangeListeners' container was not WeakHashMap, it would be very bad.If the above code was written in an Activity . Since you are using non-static (anonymous) inner class which will implicitly holds the reference of the enclosing instance. This will cause memory leak.
这可能看起来不错。但是如果 OnSharedPreferenceChangeListeners 的容器不是 WeakHashMap,那就很糟糕了。 如果上面的代码写在一个 Activity 中。由于您使用的是非静态(匿名)内部类,它将隐式保存封闭实例的引用。这会导致内存泄漏。
What's more, If you keep the listener as a field, you could use registerOnSharedPreferenceChangeListenerat the start and call unregisterOnSharedPreferenceChangeListenerin the end. But you can not access a local variable in a method out of it's scope. So you just have the opportunity to register but no chance to unregister the listener. Thus using WeakHashMap will resolve the problem. This is the way I recommend.
更重要的是,如果您将侦听器保留为一个字段,则可以在开始时使用registerOnSharedPreferenceChangeListener并在最后调用unregisterOnSharedPreferenceChangeListener。但是您不能在其范围之外的方法中访问局部变量。因此,您只有机会注册但没有机会取消注册侦听器。因此使用 WeakHashMap 将解决问题。这是我推荐的方式。
If you make the listener instance as a static field, It will avoid the memory leak caused by non-static inner class. But as the listeners could be multiple, It should be instance-related. This will reduce the cost of handling the onSharedPreferenceChangedcallback.
如果将侦听器实例设为静态字段,则可以避免非静态内部类导致的内存泄漏。但由于侦听器可能是多个,它应该是与实例相关的。这将降低处理onSharedPreferenceChanged回调的成本。
回答by shridutt kothari
While reading Word readable data shared by first app,we should
在读取第一个应用程序共享的 Word 可读数据时,我们应该
Replace
代替
getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
with
和
getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);
in second app to get updated value in second app.
在第二个应用程序中获取第二个应用程序中的更新值。
But still it is not working...
但它仍然无法正常工作......