onSharedPreferenceChanged wird nicht ausgelöst, wenn Änderungen in separaten Aktivitäten vorgenommen werden?

70

Ich habe onSharedPreferenceChangedin meiner Hauptaktivität implementiert .

Wenn ich die Einstellungen in der Hauptaktivität ändere, wird mein Ereignis ausgelöst.

Wenn ich die Einstellungen über meinen PreferenceActivityEinstellungsbildschirm ändere ( ), wird mein Ereignis NICHT ausgelöst, wenn Einstellungen geändert werden (da es sich um eine separate Aktivität und einen separaten Verweis auf sharedPreferences handelt?).

Hat jemand eine Empfehlung, wie ich diese Situation überwinden soll?

Vielen Dank!

EDIT1: Ich habe versucht, den Ereignishandler direkt in meiner Präferenzaktivität hinzuzufügen, aber er wird nie ausgelöst. Die folgende Methode wird während onCreate meiner Präferenzaktivität aufgerufen. Wenn ich Werte ändere, wird die Nachricht nie gedruckt ( msg()ist ein Wrapper für 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
        }
    });
}
Brad Hein
quelle

Antworten:

142

Der OnSharedPreferenceChangeListenerMüll wird in Ihrem Fall gesammelt, wenn Sie eine anonyme Klasse verwenden.

Um dieses Problem zu lösen, verwenden Sie den folgenden Code PreferenceActivity, um einen Änderungslistener zu registrieren und die Registrierung aufzuheben:

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
}

Beachten Sie außerdem, dass der Listener nur aufgerufen wird, wenn sich der tatsächliche Wert ändert. Wenn Sie denselben Wert erneut einstellen, wird der Listener nicht ausgelöst.

Siehe auch SharedPreferences.onSharedPreferenceChangeListener wird nicht konsistent aufgerufen

Thumbmunkeys
quelle
3
Oh Mann du rockst! Wie knifflig !!! Mein onsharedpreferencelistener wurde Müll gesammelt! Also habe ich per Link einen Langzeitverweis (globale Mitgliedsvariable) darauf erstellt und voila, es funktioniert großartig!
Brad Hein
@pivotnig Wenn ich Ihren Code ausprobiere, wird ein Kompilierungsfehler angezeigt. Dies wird nicht akzeptiert, da es sich um eine PreferenceActivity-Unterklasse handelt. Ist das etwas anderes in deinem Code?
@dpk ohne mir den Fehler zu sagen, ist es schwer zu sagen, was das Problem ist. Auf jeden Fall sollten Sie eine neue Frage zu Ihrem Problem stellen.
Thumbmunkeys
1
Bruder, du bist großartig. Danke für die Antwort. (y)
Mukul Goel
1
Gute Antwort! Sofort geholfen
Anton
17

Dies geschieht, weil Müllsammler. es funktioniert nur einmal. dann wird die Referenz als Müll gesammelt. Erstellen Sie also ein Instanzfeld für den Listener.

private OnSharedPreferenceChangeListener listner;

listner = new SharedPreferences.OnSharedPreferenceChangeListener() {        
        @Override
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
            //implementation goes here
        }
    };
    prefs.registerOnSharedPreferenceChangeListener(listner);
hderanga
quelle
1
Ich denke, diese Antwort kommt dem Problem am nächsten. Es scheint, dass der Listener Müll gesammelt wird, wenn er direkt in der Aufrufstelle definiert ist. Man sollte also einen Verweis auf den Hörer woanders aufbewahren, damit es funktioniert.
Petr Gladkikh
@hderanga Danke, das funktioniert für mich, weil meine Klasse nicht erweitert wird PreferenceActivity
user2814778
Probleme mit Speicherverlusten?
Jfly
7

Ich bin hier angekommen, wie viele andere auch, weil mein Zuhörer nicht gefeuert wird, wenn ich meinen Booleschen Wert von truein falseoder umgekehrt ändere.

Nach vielem Lesen und Umgestalten, Umschalten contexts/inner classes/privates/static/und dergleichen erkannte ich meinen (dummen) Fehler:

Das onSharedPreferenceChangedwird nur aufgerufen, wenn sich etwas ändert. Nur. Je.

Während meiner Tests war ich so dumm, die ganze Zeit auf dieselbe Schaltfläche zu klicken, sodass der Präferenz immer derselbe Boolesche Wert zugewiesen wurde, sodass er sich nie änderte.

Hoffe das hilft jemandem !!

mrArias
quelle
3

Eine andere Möglichkeit, das Problem zu vermeiden, besteht darin, Ihre Aktivität zur Listener-Klasse zu machen. Da es nur eine Überschreibungsmethode mit einem eindeutigen Namen gibt, können Sie dies tun:

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)
    {
        ...
    }
} 
Steve Waring
quelle
1
Das habe ich gemacht, aber es hat nicht funktioniert. Unter anderem habe ich festgestellt, dass die Verwendung von OnResume () und OnPause () zum Registrieren bzw. Aufheben der Registrierung des Listeners dazu führt, dass der Listener ineffektiv ist, da der Benutzer die MainActivity verlässt, wenn er die PreferenceActivity verwendet (was sinnvoll ist, wenn Sie darüber nachdenken ).
Trans
1

Beachten Sie, dass in der ursprünglichen Frage von einer MainActivity gesprochen wurde, die Änderungen in einer PreferenceActivity abhört. Der Fragesteller fügte dann ein "EDIT1" hinzu und änderte die Frage so, dass sie in der PreferenceActivity selbst abgehört wurde. Das ist einfacher als das erstere und scheint das zu sein, was alle Antworten annehmen. Aber was ist, wenn Sie immer noch das frühere Szenario wollen?

Nun, es wird auch funktionieren, aber verwenden Sie OnResume () und OnPause () nicht, um den Listener zu registrieren und die Registrierung aufzuheben. Dies führt dazu, dass der Listener ineffektiv ist, da der Benutzer die MainActivity verlässt, wenn er die PreferenceActivity verwendet (was sinnvoll ist, wenn Sie darüber nachdenken). Es wird also funktionieren, aber dann hört Ihre MainActivity auch dann im Hintergrund zu, wenn der Benutzer sie nicht verwendet. Eine Art Verschwendung von Ressourcen, nicht wahr? Es gibt also eine andere Lösung, die zu funktionieren scheint. Fügen Sie einfach eine Methode zu OnResume () hinzu, um alle Einstellungen erneut zu lesen. Wenn ein Benutzer die Bearbeitungseinstellungen in einer PreferenceActivity abgeschlossen hat, nimmt die MainActivity diese auf, wenn der Benutzer zu ihr zurückkehrt und Sie überhaupt keinen Listener benötigen .

Jemand, bitte lassen Sie mich wissen, wenn er ein Problem mit diesem Ansatz sieht.

trans
quelle
0

Warum fügen Sie onSharedPreferenceChangedin den restlichen Aktivitäten nicht einfach ein hinzu, bei dem sich die Einstellungen ändern könnten?

Cristian
quelle
Du meinst wie onSharedPreferenceChanged direkt in meiner Einstellungsaktivität definieren? Ich habe es getan und es feuert nicht.
Brad Hein
Für die Aufzeichnung wurde das Problem dadurch verursacht, dass der Garbage Collector meinen Event-Handler zurückforderte. Ich musste einen globalen Mitgliedsverweis auf den Ereignishandler erstellen.
Brad Hein
0

Der Garbage Collector löscht, dass ... Sie stattdessen einen Anwendungskontext verwenden sollten ... oder einfach den Code hinzufügen, wenn die App gestartet wird ... und dann den Listener mit dem Anwendungskontext hinzufügen ...

superUser
quelle
0

Ziehen Sie in Betracht, PreferencesChangeListener in der Instanz der Android App- Klasse zu belassen. Obwohl es sich NICHT um eine saubere Lösung handelt, sollte das Speichern von Referenzen in der App verhindern, dass GC Ihren Listener mit Müll sammelt, und Sie sollten weiterhin in der Lage sein, DB-Änderungsaktualisierungen zu erhalten. Denken Sie daran, dass der Präferenzmanager keinen starken Verweis auf den Hörer speichert! ( 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)
    }
}

und

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
        }
    }
}
kosiara - Bartosz Kosarzycki
quelle
-2

Beim Lesen von Word-lesbaren Daten, die von der ersten App gemeinsam genutzt werden, sollten wir dies tun

Ersetzen

getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);

mit

getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);

in der zweiten App, um den aktualisierten Wert in der zweiten App zu erhalten.

Shridutt Kothari
quelle
1
Android unterstützt den Zugriff SharedPreferencesvon mehreren Prozessen nicht. Dies führt zu Parallelitätsproblemen, die dazu führen können, dass alle Einstellungen verloren gehen. Wird auch MODE_MULTI_PROCESSnicht mehr unterstützt.
Sam