私は次のように設定変更リスナーを登録しています (onCreate()
メイン アクティビティの 内):
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(
SharedPreferences prefs, String key) {
System.out.println(key);
}
});
問題は、リスナーが常に呼び出されるわけではないことです。設定が変更された最初の数回は機能しますが、その後はアプリをアンインストールして再インストールするまで呼び出されなくなります。アプリケーションをいくら再起動しても、問題は解決しないようです。
メーリングリストを見つけました糸同じ問題を報告しましたが、誰も答えてくれませんでした。何が間違っているのでしょうか?
ベストアンサー1
これは巧妙なものです。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のドキュメントは更新しましたと警告この動作について。奇妙な動作は残っていますが、今では文書化されています。