1. ホーム
  2. アンドロイド

[解決済み】SharedPreferences.onSharedPreferenceChangeListenerが一貫して呼び出されない。

2022-04-18 13:41:40

質問

私はこのようにプリファレンス変更リスナーを登録しています。 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のドキュメントは 更新 警告 この動作について ですから、奇妙な動作は残っています。しかし、今は文書化されています。