web-dev-qa-db-fra.com

onSharedPreferenceChanged n'est pas déclenché si un changement survient dans une activité distincte?

J'ai implémenté onSharedPreferenceChanged dans mon activité principale. 

Si je modifie les préférences dans l'activité principale, mon événement est déclenché. 

Si je modifie les préférences via mon écran de préférences (PreferenceActivity), mon événement ne se déclenche PAS lorsque les préférences sont modifiées (car il s'agit d'une activité distincte et d'une référence distincte aux références partagées?)

Quelqu'un a-t-il une recommandation sur la manière dont je devrais agir pour surmonter cette situation? 

Merci!

EDIT1: J'ai essayé d'ajouter le gestionnaire d'événements directement dans mon activité de préférence, mais il ne s'est jamais déclenché. La méthode suivante est appelée lors de l’activité de création de mon activité onCreate. Lorsque je change de valeur, le message n'est jamais imprimé (msg() est un wrapper pour Log.d). 

private void registerChangeListener () {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);

    sp.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener () {
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            msg (" ***** Shared Preference Update ***** ");
            Intent i = new Intent();
            i.putExtra("KEY", key);
            i.setAction("com.gtosoft.dash.settingschanged");

            sendBroadcast(i);

            // TODO: fire off the event
        }
    });
}
66
Brad Hein

Si vous utilisez une classe anonyme, la variable OnSharedPreferenceChangeListener est collectée dans votre cas.

Pour résoudre ce problème, utilisez le code suivant dans PreferenceActivity pour enregistrer et désenregistrer un écouteur de modification:

public class MyActivity extends PreferenceActivity implements
    OnSharedPreferenceChangeListener {

@Override
protected void onResume() {
    super.onResume();
    // Set up a listener whenever a key changes
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
    super.onPause();
    // Unregister the listener whenever a key changes
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}

public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key) 
{
  // do stuff
}

De plus, sachez que l'auditeur n'est appelé que si la valeur réelle change. Redéfinir la même valeur ne déclenchera pas l'auditeur.

voir aussi SharedPreferences.onSharedPreferenceChangeListener n'étant pas appelé de manière cohérente

132
thumbmunkeys

Cela se produit parce que garbage collector. cela ne fonctionne qu'une fois. alors la référence est collectée comme une poubelle. alors créez un champ d'instance pour le programme d'écoute. 

private OnSharedPreferenceChangeListener listner;

listner = new SharedPreferences.OnSharedPreferenceChangeListener() {        
        @Override
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            //implementation goes here
        }
    };
    prefs.registerOnSharedPreferenceChangeListener(listner);
14
hderanga

Je suis arrivé ici, comme beaucoup d'autres, parce que mon auditeur ne sera pas viré lorsque je changerai son booléen de true à false, ou vice-versa.

Après beaucoup de lecture et de refactorisation, en passant de contexts/inner à classes/privates/static/ et similaires, j'ai réalisé mon erreur (stupide):

La onSharedPreferenceChanged est seulement appelé si quelque chose change. Seulement. Déjà.

Au cours de mes tests, j'étais si bête de cliquer tout le temps sur le même bouton, en assignant ainsi la même valeur booléenne à la préférence tout le temps, afin qu'elle ne change jamais.

J'espère que ça aide quelqu'un !!

6
mrArias

Une autre façon d’éviter le problème est de faire de votre activité la classe d’auditeur. Comme il n'y a qu'une seule méthode de substitution avec un nom distinctif, vous pouvez le faire:

public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sharedPreferences.registerOnSharedPreferenceChangeListener(this);
        ...
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
    {
        ...
    }
} 
2
Steve Waring

Notez que la question initiale parlait d'une MainActivity écoutant les modifications de paramètres dans une PreferenceActivity. Le demandeur a ensuite ajouté "EDIT1" et a changé la question pour écouter dans PreferenceActivity lui-même. C'est plus facile que l'ancien et semble être ce que toutes les réponses supposent. Mais que faire si vous voulez toujours l'ancien scénario?

Eh bien, cela fonctionnera aussi, mais n'utilisez pas OnResume () et OnPause () pour enregistrer et annuler l'enregistrement de l'écouteur. Cela risquerait de rendre l'auditeur inefficace, car l'utilisateur quitte MainActivity lorsqu'il utilise PreferenceActivity (ce qui est logique lorsque vous y réfléchissez). Cela fonctionnera donc, mais votre MainActivity écoutera toujours en arrière-plan, même lorsque l’utilisateur ne l’utilise pas. Un gaspillage de ressources, n'est-ce pas? Donc, il y a une autre solution qui semble fonctionner, ajoutez simplement une méthode à OnResume () pour relire toutes les préférences. Ainsi, lorsqu'un utilisateur finit de modifier les préférences dans PreferenceActivity, MainActivity les reprendra lorsque l'utilisateur y reviendra et que vous n'avez pas besoin d'un écouteur du tout .

Quelqu'un s'il vous plaît faites le moi savoir si ils voient un problème avec cette approche.

1
trans

Pourquoi ne pas simplement ajouter une onSharedPreferenceChanged dans le reste des activités où les préférences pourraient changer?

0
Cristian

Le ramasse-miettes efface cela ... vous devriez plutôt envisager d'utiliser un contexte d'application ... ou tout simplement ajouter le code au lancement de l'application ... puis ajoutez l'écouteur avec le contexte d'application ...

0
superUser

Pensez à conserver PreferencesChangeListener dans l'instance Android App class. Bien que ce ne soit PAS une solution propre stockant une référence dans App, cela devrait empêcher le GC de récupérer votre auditeur et vous devriez toujours pouvoir recevoir les mises à jour des modifications de base de données. Rappelez-vous que le gestionnaire de préférences ne pas stocke une référence forte à l'auditeur! ( WeakHashMap )

/**
 * Main application class
 */
class MyApp : Application(), KoinComponent {

    var preferenceManager: SharedPreferences? = null
    var prefChangeListener: MySharedPrefChangeListener? = null

    override fun onCreate() {
        super.onCreate()

        preferenceManager = PreferenceManager.getDefaultSharedPreferences(this)
        prefChangeListener = MySharedPrefChangeListener()
        preferenceManager?.registerOnSharedPreferenceChangeListener(prefChangeListener)
    }
}

et

class MySharedPrefChangeListener : SharedPreferences.OnSharedPreferenceChangeListener {

    /**
     * Called when a shared preference is changed, added, or removed.
     */
    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
        if (sharedPreferences == null)
            return

        if (sharedPreferences.contains(key)) {
            // action to perform
        }
    }
}