SharedPreferences.onSharedPreferenceChangeListener不被一致调用

我正在注册一个首选项更改监听器(在我的主要活动的onCreate() ):

 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs.registerOnSharedPreferenceChangeListener( new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged( SharedPreferences prefs, String key) { System.out.println(key); } }); 

麻烦的是,听众并不总是被调用。 它适用于前几次偏好改变,然后不再调用,直到我卸载并重新安装应用程序。 没有任何重新启动应用程序似乎修复它。

我发现一个邮件列表线程报告同样的问题,但没有人真正回答他。 我究竟做错了什么?

这是一个鬼鬼祟祟的。 SharedPreferences将监听器保存在WeakHashMap中。 这意味着你不能使用一个匿名的内部类作为监听器,因为一旦你离开当前的作用域,它将成为垃圾收集的目标。 它会在第一时间工作,但最终会收集垃圾,从WeakHashMap中删除并停止工作。

在你的类的字段中保留一个对监听器的引用,只要你的类实例没有被销毁,你就可以。

即而不是:

 prefs.registerOnSharedPreferenceChangeListener( new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // Implementation } }); 

做这个:

 // 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); 

在onDestroy方法中取消注册的原因解决了问题,因为要这样做,您必须将侦听器保存在一个字段中,从而避免了这个问题。 这是在解决问题的领域中保存监听器,而不是在onDestroy中取消注册。

更新 :Android文档已更新有关此行为的警告 。 所以,古怪的行为仍然存在。 但现在它被记录。

由于这是我想要添加我的50ct的主题最详细的页面。

我有没有调用OnSharedPreferenceChangeListener的问题。 我的SharedPreferences在主Activity的开始被检索:

 prefs = PreferenceManager.getDefaultSharedPreferences(this); 

我的PreferenceActivity代码很短,除了显示首选项之外什么也不做:

 public class Preferences extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // load the XML preferences file addPreferencesFromResource(R.xml.preferences); } } 

每次按下菜单按钮,我都会从主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; } 

请注意 ,在这种情况下,注册OnSharedPreferenceChangeListener需要在创建PreferenceActivity之后完成,否则主Activity中的Handler将不会被调用! 我花了一些甜蜜的时间才意识到…

这个接受的答案是可以的,对我来说,每次活动恢复时都会创建新的实例

那么如何在活动中保持对听众的引用

 OnSharedPreferenceChangeListener myPrefListner = new OnSharedPreferenceChangeListener(){ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // your stuff } }; 

并在你的onResume和onPause

 @Override protected void onResume() { super.onResume(); getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(myPrefListner); } @Override protected void onPause() { super.onPause(); getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(myPrefListner); } 

这与你所做的非常相似,除非我们保持一个很好的参考。

听者保存在WeakHashMap中是有意义的。因为大部分时间,开发人员更喜欢这样写代码。

 PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener( new OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key); } }); 

这可能看起来不错。 但是,如果OnSharedPreferenceChangeListeners的容器不是WeakHashMap,那将是非常糟糕的。如果上面的代码写在一个Activity中。 由于您使用的是非静态(匿名)内部类,它将隐式地保存封闭实例的引用。 这将导致内存泄漏。

更重要的是,如果将侦听器保存为字段,则可以在开始时使用registerOnSharedPreferenceChangeListener ,最后调用unregisterOnSharedPreferenceChangeListener 。 但是你不能在一个超出范围的方法中访问局部变量。 所以你有机会注册,但没有机会注销听众。 因此使用WeakHashMap将解决这个问题。 这是我推荐的方式。

如果将侦听器实例设置为静态字段,则可以避免由非静态内部类引起的内存泄漏。 但是,由于听众可以是多个,所以应该与实例相关。 这将降低处理onSharedPreferenceChanged回调的成本。

在阅读由第一个应用程序共享的Word可读数据时,我们应该

更换

 getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE); 

 getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS); 

在第二个应用程序中获取更新值在第二个应用程序

但仍然不工作…