studio - para que sirve shared preference en android
SharedPreferences.onSharedPreferenceChangeListener no se llama de forma coherente (7)
Estoy registrando un oyente de cambio de preferencia como este (en el onCreate()
de mi actividad principal):
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(
SharedPreferences prefs, String key) {
System.out.println(key);
}
});
El problema es que el oyente no siempre es llamado. Funciona las primeras veces que se cambia una preferencia, y luego deja de llamarse hasta que desinstale y reinstale la aplicación. Ninguna cantidad de reinicio de la aplicación parece arreglarlo.
Encontré un thread lista de correo informando el mismo problema, pero nadie realmente le respondió. ¿Qué estoy haciendo mal?
Como esta es la página más detallada para el tema, quiero agregar mi 50ct.
Tuve el problema de que OnSharedPreferenceChangeListener no fue llamado. Mis SharedPreferences se recuperan al inicio de la actividad principal por:
prefs = PreferenceManager.getDefaultSharedPreferences(this);
Mi código de PreferenceActivity es corto y no hace nada excepto mostrar las preferencias:
public class Preferences extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// load the XML preferences file
addPreferencesFromResource(R.xml.preferences);
}
}
Cada vez que se presiona el botón de menú, creo la Actividad de Preferencia de la Actividad principal:
@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;
}
Tenga en cuenta que el registro de OnSharedPreferenceChangeListener se debe realizar DESPUÉS de crear la Actividad de Preferencia en este caso, de lo contrario, ¡¡¡no se llamará al Controlador en la Actividad principal !!! Me tomó un tiempo dulce para darme cuenta de que ...
Entonces, no sé si esto realmente ayudaría a alguien, resolvió mi problema. A pesar de que había implementado OnSharedPreferenceChangeListener
como lo indica la respuesta aceptada . Aún así, tuve una inconsistencia con el oyente que se llama.
Vine aquí para comprender que el Android solo lo envía para la recolección de basura después de un tiempo. Entonces, miré mi código. Para mi vergüenza, no había declarado al oyente GLOBALMENTE, sino que dentro de onCreateView
. Y eso fue porque escuché a Android Studio diciéndome que convierta al oyente en una variable local.
Esta es una astuta SharedPreferences mantiene a los oyentes en un WeakHashMap. Esto significa que no puede usar una clase interna anónima como oyente, ya que se convertirá en el objetivo de la recolección de basura tan pronto como abandone el ámbito actual. Al principio funcionará, pero al final, se recogerá la basura, se eliminará de WeakHashMap y dejará de funcionar.
Mantenga una referencia al oyente en un campo de su clase y estará bien, siempre que su instancia de clase no se destruya.
es decir, en lugar de
prefs.registerOnSharedPreferenceChangeListener(
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Implementation
}
});
hacer esto:
// 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);
La razón por la que no se registra en el método onDestroy soluciona el problema se debe a que para ello tenía que guardar el agente de escucha en un campo, evitando así el problema. Es el guardado del oyente en un campo que soluciona el problema, no el registro en onDestroy.
ACTUALIZACIÓN : Los documentos de Android se han updated con warnings sobre este comportamiento. Por lo tanto, el comportamiento de bicho raro permanece. Pero ahora está documentado.
Esta respuesta aceptada está bien, ya que para mí está creando una nueva instancia cada vez que se reanuda la actividad.
Entonces, ¿qué hay de mantener la referencia al oyente dentro de la actividad?
OnSharedPreferenceChangeListener myPrefListner = new OnSharedPreferenceChangeListener(){
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// your stuff
}
};
y en tu onResume y onPause
@Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(myPrefListner);
}
@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(myPrefListner);
}
Esto será muy similar a lo que está haciendo, excepto que estamos manteniendo una referencia difícil.
La respuesta aceptada crea un SharedPreferenceChangeListener cada vez que se llama onResume (). @Samuel lo resuelve al hacer que SharedPreferenceListener sea miembro de la clase Activity. Pero hay una tercera y una solución más sencilla que Google también utiliza en este código de código . Haga que su clase de actividad implemente la interfaz OnSharedPreferenceChangeListener
e onSharedPreferenceChanged
en la Actividad, haciendo que la Actividad en sí sea un 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);
}
}
Mientras leemos datos legibles de Word compartidos por la primera aplicación, deberíamos
Reemplazar
getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
con
getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);
en la segunda aplicación para obtener valor actualizado en la segunda aplicación.
Pero aún no está funcionando ...
Tiene sentido que los oyentes se mantengan en WeakHashMap. Debido a que la mayoría de las veces, los desarrolladores prefieren escribir el código de esta manera.
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).registerOnSharedPreferenceChangeListener(
new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
Log.i(LOGTAG, "testOnSharedPreferenceChangedWrong key =" + key);
}
});
Esto puede parecer no malo. Pero si el contenedor de OnSharedPreferenceChangeListeners no fuera WeakHashMap, sería muy malo. Si el código anterior se escribiera en una actividad. Ya que está utilizando una clase interna no estática (anónima) que implícitamente contiene la referencia de la instancia adjunta. Esto causará pérdida de memoria.
Además, si mantiene al oyente como un campo, puede usar registerOnSharedPreferenceChangeListener al comienzo y llamar a unregisterOnSharedPreferenceChangeListener al final. Pero no puede acceder a una variable local en un método fuera de su alcance. Así que solo tiene la oportunidad de registrarse, pero no la posibilidad de anular el registro del oyente. De este modo, el uso de WeakHashMap resolverá el problema. Así lo recomiendo.
Si realiza la instancia de escucha como un campo estático, evitará la pérdida de memoria causada por una clase interna no estática. Pero como los oyentes pueden ser múltiples, debería estar relacionado con la instancia. Esto reducirá el costo de manejo de la devolución de llamada onSharedPreferenceChanged .