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);
在第二个应用程序中获取更新值在第二个应用程序
但仍然不工作…