[android] 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의 등록 해제가 아니라 문제를 해결하는 필드에 리스너를 저장하는 것입니다.
UPDATE : 안드로이드 문서는 한 업데이트 와 경고 이 문제에 대한. 따라서 이상한 행동이 남아 있습니다. 그러나 이제 문서화되었습니다.
답변
이 수락 된 대답은 괜찮 습니다. 활동이 재개 될 때마다 새 인스턴스를 만들고 있습니다.
그래서 활동 내에서 리스너에 대한 참조를 유지하는 것은 어떻습니까?
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);
}
이것은 우리가 단단한 참조를 유지하는 것을 제외하고는 당신이하고있는 것과 매우 유사합니다.
답변
이 주제에 대한 가장 자세한 페이지이므로 50ct를 추가하고 싶습니다.
OnSharedPreferenceChangeListener가 호출되지 않았다는 문제가있었습니다. 내 SharedPreferences는 기본 활동 시작시 다음을 통해 검색됩니다.
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);
}
}
메뉴 버튼을 누를 때마다 기본 활동에서 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를 생성 후 수행 할 것으로는, 다른 핸들러는 주요 활동에 호출되지 않습니다! 그것을 깨닫는 데 약간의 시간이 걸렸습니다 …
답변
전화를받을 SharedPreferenceChangeListener
때마다 수락 된 답변이 생성 됩니다 onResume
. @SamuelSharedPreferenceListener
은 Activity 클래스의 멤버를 만들어 해결합니다 . 그러나 Google 이이 코드 랩 에서 사용 하는 세 번째 및보다 간단한 솔루션 이 있습니다 . 액티비티 클래스가 OnSharedPreferenceChangeListener
인터페이스를 구현하고 onSharedPreferenceChanged
액티비티에서 재정 의하여 액티비티 자체를 효과적으로 만듭니다 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);
}
}
답변
저장된 키에서 변경이 발생할 때 감지하는 SharedPreferenceChangeListener 등록을위한 Kotlin 코드 :
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener { sharedPreferences, key ->
if(key=="language") {
//Do Something
}
}
이 코드를 onStart () 또는 다른 곳에 넣을 수 있습니다. * 사용할 것을 고려하십시오
if(key=="YourKey")
또는 sharedPreferences의 다른 키에서 발생하는 모든 변경에 대해 “// Do Something”블록 내의 코드가 잘못 실행됩니다.
답변
따라서 이것이 실제로 누군가를 도울 수 있는지 모르겠습니다. 문제가 해결되었습니다. 비록 내가 받아 들인 대답에OnSharedPreferenceChangeListener
명시된대로 구현했지만 . 그래도 청취자의 전화가 불일치했습니다.
나는 안드로이드가 얼마 후 가비지 수집을 위해 그것을 보낸다는 것을 이해하기 위해 여기에 왔습니다. 그래서 코드를 살펴 보았습니다. 부끄러운 일로 , 리스너 GLOBALLY를 선언하지 않고 대신 onCreateView
. 리스너를 로컬 변수로 변환하라는 Android Studio를 들었 기 때문입니다.
답변
리스너는 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 콜백 처리 비용을 줄 입니다.