SharedPreferences.onSharedPreferenceChangeListener未被一致地调用

syn*_*nic 250 android android-preferences

我正在注册这样的偏好更改监听器(在onCreate()我的主要活动中):

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

prefs.registerOnSharedPreferenceChangeListener(
   new SharedPreferences.OnSharedPreferenceChangeListener() {
       public void onSharedPreferenceChanged(
         SharedPreferences prefs, String key) {

         System.out.println(key);
       }
});
Run Code Online (Sandbox Code Playgroud)

麻烦的是,听众并不总是被召唤.它最初几次更改首选项,然后在卸载并重新安装应用程序之前不再调用它.没有重新启动应用程序似乎修复它.

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

Bla*_*nka 591

这是一个偷偷摸摸的.SharedPreferences将侦听器保留在WeakHashMap中.这意味着您不能将匿名内部类用作侦听器,因为一旦离开当前作用域,它将成为垃圾收集的目标.它最初会工作,但最终会收集垃圾,从WeakHashMap中删除并停止工作.

在类的字段中保持对侦听器的引用,如果您的类实例未被销毁,您就可以了.

即代替:

prefs.registerOnSharedPreferenceChangeListener(
  new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // Implementation
  }
});
Run Code Online (Sandbox Code Playgroud)

做这个:

// 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);
Run Code Online (Sandbox Code Playgroud)

在onDestroy方法中取消注册的原因解决了问题,因为要做到这一点,你必须在一个字段中保存监听器,从而防止出现问题.这是在一个字段中保存侦听器来解决问题,而不是在onDestroy中取消注册.

更新:Android文档已更新,并显示有关此行为的警告.所以,奇怪的行为仍然存在.但现在已经记录在案了.

  • 这让我很难过,我以为自己正在失去理智.感谢您发布此解决方案! (18认同)
  • 这篇文章很棒,非常感谢,这可能花费我几个小时的不可能调试! (10认同)
  • 我希望我可以投票1000次.穆乔谢谢你! (7认同)
  • 很好的答案,谢谢.绝对应该在文档中提到.https://code.google.com/p/android/issues/detail?id=48589 (5认同)

Sam*_*uel 15

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

那么如何在活动中保持对监听器的引用呢

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

并在你的onResume和onPause

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



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

}
Run Code Online (Sandbox Code Playgroud)

除了我们保持一个硬参考之外,这与你正在做的非常相似.


Bim*_*Bim 15

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

我遇到的问题是没有调用OnSharedPreferenceChangeListener.我的SharedPreferences在主Activity的开头被检索:

prefs = PreferenceManager.getDefaultSharedPreferences(this);
Run Code Online (Sandbox Code Playgroud)

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

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

每次按下菜单按钮,我都会从主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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下,在创建PreferenceActivity之后需要注册OnSharedPreferenceChangeListener,否则将不会调用主Activity中的Handler!我花了很多时间才意识到......


Pra*_*anD 6

接受的答案将创建一个SharedPreferenceChangeListener每次onResume调用。@Samuel通过使其成为SharedPreferenceListenerActivity类的成员来解决该问题。但是Google此代码实验室中也使用了第三个更直接的解决方案。使您的活动类实现OnSharedPreferenceChangeListener接口并onSharedPreferenceChanged在Activity中进行覆盖,从而有效地使Activity本身成为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);
    }
}
Run Code Online (Sandbox Code Playgroud)