Umgang mit Lebenszyklen mit lebenszyklusbezogenen Komponenten Teil von Android Jetpack

Lebenszyklusbewusste Komponenten führen Aktionen als Reaktion auf eine Änderung des Lebenszyklusstatus einer anderen Komponente, wie z. B. Aktivitäten und Fragmente. Diese -Komponenten helfen Ihnen, besser organisierten und oft leichteren Code zu erstellen. die einfacher zu verwalten ist.

Ein gängiges Muster ist die Implementierung der Aktionen der abhängigen Komponenten in der Lebenszyklusmethoden von Aktivitäten und Fragmenten. Dieses Muster führt jedoch zu einer schlechte Strukturierung des Codes und die Zunahme von Fehlern. Durch die Verwendung von können Sie den Code abhängiger Komponenten Lebenszyklusmethoden und die Komponenten selbst.

Die androidx.lifecycle -Paket enthält Klassen und Schnittstellen, mit denen Sie lebenszyklusorientierte Komponenten, also Komponenten, die ihre basierend auf dem aktuellen Lebenszyklusstatus einer Aktivität oder eines Fragments.

Die meisten im Android-Framework definierten App-Komponenten haben Lebenszyklen angehängt. Lebenszyklen werden vom Betriebssystem oder den Framework-Code, der in Ihrem Prozess ausgeführt wird. Sie sind das Herzstück der Funktionsweise von Android und Ihre Anwendung muss diese berücksichtigen. Andernfalls können Speicherlecks oder sogar App-Abstürze.

Angenommen, wir haben eine Aktivität, bei der der Gerätestandort auf dem Bildschirm angezeigt wird. A Beispiel für eine gängige Implementierung:

Kotlin

internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) {

    fun start() {
        // connect to system location service
    }

    fun stop() {
        // disconnect from system location service
    }
}

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        myLocationListener.start()
        // manage other components that need to respond
        // to the activity lifecycle
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

Java

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

Auch wenn dieses Beispiel gut aussieht, haben Sie in einer echten App zu viele Aufrufe, die die UI und andere Komponenten als Reaktion auf den aktuellen Status verwalten des Lebenszyklus. Bei der Verwaltung mehrerer Komponenten Code in Lebenszyklusmethoden wie onStart() und onStop(), was ihre Verwaltung erschwert.

Darüber hinaus gibt es keine Garantie, dass die Komponente vor der Aktivität oder Fragment gestoppt. Dies gilt insbesondere, wenn wir eine lang andauernden Vorgang, z. B. eine Konfigurationsprüfung in onStart(). Dies kann zu einer Race-Bedingung führen, bei der die onStop()-Methode vor dem onStart() beendet wird, wodurch die Komponente länger als vorgesehen aktiv bleibt. erforderlich.

Kotlin

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        Util.checkUserStatus { result ->
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start()
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
    }

}

Java

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

Die androidx.lifecycle -Paket enthält Klassen und Schnittstellen, mit denen Sie diese Probleme in einem belastbar und isoliert.

Lebenszyklus

Lifecycle ist ein Kurs der die Informationen zum Lebenszyklusstatus einer Komponente enthält, z. B. Aktivität oder ein Fragment) und ermöglicht es anderen Objekten, diesen Status zu beobachten.

In Lifecycle werden zwei Haupt- Aufzählungen, um den Lebenszyklusstatus der zugehörigen Komponente zu verfolgen:

Veranstaltung
Die Lebenszyklusereignisse, die vom Framework und vom Klasse Lifecycle. Diese -Ereignisse den Callback-Ereignissen in Aktivitäten und Fragmenten.
Bundesland
Der aktuelle Status der Komponente, die vom Objekt Lifecycle.
<ph type="x-smartling-placeholder">
</ph> Diagramm der Lebenszyklusstatus
<ph type="x-smartling-placeholder">
</ph> Abbildung 1: Status und Ereignisse, die die Android-Aktivität ausmachen Lebenszyklus

Stellen Sie sich die Zustände als Knoten eines Graphen und die Ereignisse als Ränder zwischen dieser Knoten.

Eine Klasse kann den Lebenszyklusstatus der Komponente überwachen, DefaultLifecycleObserver und entsprechende Methoden wie onCreate, onStart usw. überschreiben. Dann können Sie einen Beobachter hinzufügen, indem Sie die Methode addObserver() von Lifecycle und übergeben eine Instanz Ihres Beobachters, wie unten Beispiel:

Kotlin

class MyObserver : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        connect()
    }

    override fun onPause(owner: LifecycleOwner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(MyObserver())

Java

public class MyObserver implements DefaultLifecycleObserver {
    @Override
    public void onResume(LifecycleOwner owner) {
        connect()
    }

    @Override
    public void onPause(LifecycleOwner owner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

Im obigen Beispiel implementiert das myLifecycleOwner-Objekt den Parameter LifecycleOwner die im folgenden Abschnitt erläutert wird.

LifecycleOwner

LifecycleOwner ist ein Schnittstelle für eine einzelne Methode, die anzeigt, dass die Klasse über ein Lifecycle Es hat eine Methode getLifecycle(), der von der Klasse implementiert werden muss. Wenn Sie den Lebenszyklus einer ganzen Anwendung verwalten möchten finden Sie unter ProcessLifecycleOwner

Diese Schnittstelle abstrahiert die Inhaberschaft eines Lifecycle von Einzelpersonen wie Fragment und AppCompatActivity und ermöglicht das Schreiben von Komponenten, mit ihnen zusammenzuarbeiten. Jede benutzerdefinierte Anwendungsklasse kann den LifecycleOwner .

Komponenten, die DefaultLifecycleObserver nahtlos mit Komponenten arbeiten, LifecycleOwner weil ein Inhaber einen Lebenszyklus bereitstellen kann, den ein Beobachter registrieren kann. ansehen.

Für das Beispiel mit dem Standort-Tracking können wir die MyLocationListener-Klasse DefaultLifecycleObserver implementieren und dann mit der Methode Lifecycle in der Methode onCreate() Dadurch können die MyLocationListener-Klasse als eigenständig, d. h., die Logik zur Stattdessen wird in MyLocationListener deklariert, dass auf Änderungen des Lebenszyklusstatus reagiert wird. der Aktivität. Wenn die einzelnen Komponenten ihre eigene Logik speichern, Aktivitäten und Fragmentierungslogik einfacher zu verwalten.

Kotlin

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this, lifecycle) { location ->
            // update UI
        }
        Util.checkUserStatus { result ->
            if (result) {
                myLocationListener.enable()
            }
        }
    }
}

Java

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}

Ein häufiger Anwendungsfall besteht darin, das Aufrufen bestimmter Callbacks zu vermeiden, wenn der Lifecycle ist schlecht Bundesstaat in Bearbeitung. Wenn der Callback z. B. eine Fragment-Transaktion nach wenn der Aktivitätsstatus gespeichert ist, würde das einen Absturz auslösen. rufen Sie diesen Callback auf.

Um diesen Anwendungsfall zu vereinfachen, Lifecycle-Klasse ermöglicht andere Objekte, um den aktuellen Status abzufragen.

Kotlin

internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver {

    private var enabled = false

    override fun onStart(owner: LifecycleOwner) {
        if (enabled) {
            // connect
        }
    }

    fun enable() {
        enabled = true
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            // connect if not connected
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        // disconnect if connected
    }
}

Java

class MyLocationListener implements DefaultLifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @Override
    public void onStart(LifecycleOwner owner) {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @Override
    public void onStop(LifecycleOwner owner) {
        // disconnect if connected
    }
}

Mit dieser Implementierung ist unsere LocationListener-Klasse vollständig Lebenszyklus-bewusst ist. Wenn die LocationListener aus einer anderen Aktivität benötigt wird oder Fragment, müssen wir es nur initialisieren. Die gesamte Einrichtung und das Entfernen werden von der Klasse selbst verwaltet.

Wenn eine Bibliothek Klassen bereitstellt, die mit dem Android-Lebenszyklus funktionieren müssen, empfehlen, Lebenszyklus-bewusste Komponenten zu verwenden. Ihre Bibliothekskunden können diese Komponenten ohne manuelle Lebenszyklusverwaltung Clientseite.

Benutzerdefinierten LifecycleOwner implementieren

Fragmente und Aktivitäten in der Support Library 26.1.0 und höher werden bereits implementiert. LifecycleOwner .

Wenn Sie einen benutzerdefinierten Kurs haben, LifecycleOwner, ich kann das Lebenszyklusregistrierung Klasse, aber Sie müssen Termine an diesen Kurs weiterleiten, wie im Folgenden gezeigt: Codebeispiel:

Kotlin

class MyActivity : Activity(), LifecycleOwner {

    private lateinit var lifecycleRegistry: LifecycleRegistry

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }

    public override fun onStart() {
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    }

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

Java

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

Best Practices für lebenszyklusbewusste Komponenten

  • Halten Sie Ihre UI-Controller (Aktivitäten und Fragmente) so schlank wie möglich. Sie sollten nicht versuchen, an ihre eigenen Daten zu gelangen; verwenden Sie stattdessen eine ViewModel dazu, und beobachten Sie einen LiveData um die Änderungen in den Ansichten widerzuspiegeln.
  • Versuchen Sie, datengesteuerte Benutzeroberflächen zu schreiben, für die Ihr UI-Controller dafür verantwortlich ist, Aktualisieren Sie die Ansichten, wenn sich Daten ändern, oder informieren Sie Nutzer über Aktionen ViewModel
  • Setzen Sie Ihre Datenlogik in Ihre Klasse ViewModel. ViewModel soll ausgeliefert werden als Verbindungselement zwischen dem UI-Controller und dem Rest der Anwendung. Seien aber nicht unbedingt ViewModel dafür verantwortlich, Daten abzurufen (z. B. aus einem Netzwerk). Stattdessen ViewModel sollte Folgendes aufrufen: um die Daten abzurufen, und geben das Ergebnis an die UI-Controller.
  • Mit der Datenbindung lässt sich Folgendes verwalten: eine saubere Schnittstelle zwischen den Ansichten und dem UI-Controller. So können Sie Machen Sie Ihre Ansichten deklarativer und minimieren Sie den Aktualisierungscode, den Sie aktualisieren müssen. Aktivitäten und Fragmente notieren. Wenn Sie es lieber in der Java- eine Bibliothek wie Buttermesser, um Textbausteine zu vermeiden und haben eine bessere Abstraktion.
  • Wenn Ihre Benutzeroberfläche komplex ist, sollten Sie eine Vortragender zum Verarbeiten von UI-Änderungen. Dies mag eine mühsame Aufgabe sein, um Ihre UI-Komponenten einfacher zu testen.
  • Verweisen Sie nicht auf View oder Activity. Kontext in ViewModel. Wenn ViewModel die Aktivität überdauert (im Fall von Konfigurationsänderungen), und Ihre Aktivitäten nicht ordnungsgemäß entsorgt werden.
  • Kotlin-Koroutinen zur Verwaltung verwenden lang andauernde Aufgaben und andere Vorgänge, die asynchron ausgeführt werden können.

Anwendungsfälle für lebenszyklusbezogene Komponenten

Komponenten, die den Lebenszyklus berücksichtigen, können die Verwaltung von Lebenszyklen erheblich vereinfachen. in unterschiedlichen Fällen. Hier einige Beispiele:

  • Zwischen groben und detaillierten Standortaktualisierungen wechseln Verwenden Sie Lebenszyklus-bewusste Komponenten, um präzise Standortaktualisierungen zu ermöglichen, während Ihre Standort-App ist sichtbar und wechselt zu ungenauen Updates, wenn die App im Hintergrund. LiveData, eine Komponente, die den Lebenszyklus berücksichtigt, Ermöglicht Ihrer App, die Benutzeroberfläche automatisch zu aktualisieren, wenn sich der Nutzer ändert Standorte.
  • Stoppen und Starten der Video-Pufferung Verwenden Sie Lebenszyklus-orientierte Komponenten, das Video wird so schnell wie möglich gepuffert, aber die Wiedergabe verschieben, bis die App vollständig geladen ist begonnen. Sie können auch Komponenten verwenden, die den Lebenszyklus berücksichtigen, um die Zwischenspeicherung zu beenden. wenn Ihre App gelöscht wird.
  • Netzwerkverbindung wird gestartet und beendet. Lebenszyklusbewusste Komponenten nutzen, Live-Aktualisierung (Streaming) von Netzwerkdaten aktivieren, während sich eine App im und automatisch pausiert, wenn die App Hintergrund.
  • Animierte Drawables pausieren und fortsetzen Lebenszyklusbewusste Komponenten nutzen, animierte Drawables pausieren, wenn die App im Hintergrund läuft Du kannst die Drawables wieder aktivieren, wenn die App im Vordergrund ausgeführt wird.

Umgang mit Stoppereignissen

Wenn ein Lifecycle zu einem AppCompatActivity gehört oder Fragment, die Lifecycle Statusänderungen zu CREATED und ON_STOP wird ausgelöst, wenn AppCompatActivity oder onSaveInstanceState() von Fragment aufgerufen wird.

Wenn der Status eines Fragment- oder AppCompatActivity-Elements über onSaveInstanceState(), es ist die Benutzeroberfläche gilt als unveränderlich bis ON_START ist aufgerufen. Der Versuch, die Benutzeroberfläche nach dem Speichern des Status zu ändern, führt wahrscheinlich Uneinheitlichkeiten im Navigationsstatus deiner App, weshalb FragmentManager eine Ausnahme auslöst, wenn die App eine FragmentTransaction nach dem Speichern des Status. Weitere Informationen finden Sie unter commit().

LiveData verhindert, dass dieser Grenzfall sofort einsatzbereit ist, den Beobachter nicht mehr aufrufen, wenn der zugehörige Lifecycle ist nicht mindestens STARTED Hinter den Kulissen ruft er isAtLeast() bevor der Beobachter aufgerufen wird.

Die onStop()-Methode von AppCompatActivity wird leider nach aufgerufen. onSaveInstanceState(), Dadurch entsteht eine Lücke, in der Änderungen des UI-Status zwar nicht zulässig sind, Lifecycle wurde noch nicht in den CREATED Bundesstaat.

Um dieses Problem zu vermeiden, hat die Klasse Lifecycle in Version beta2 und kennzeichnen Sie den Bundesstaat CREATED ohne das Ereignis auszulösen, sodass jeder Code, der die aktuelle erhält den tatsächlichen Wert, auch wenn das Ereignis erst gesendet wird, wenn onStop() vom System aufgerufen wird.

Leider bringt diese Lösung zwei größere Probleme mit sich:

  • Auf API-Level 23 und niedriger speichert das Android-System den Zustand eines auch dann, wenn sie teilweise durch eine andere Aktivität abgedeckt ist. In anderen ruft das Android-System onSaveInstanceState() auf. ruft aber nicht unbedingt onStop() auf. Dadurch entsteht eine potenzielle langen Intervall, bei dem der Beobachter noch glaubt, dass der Lebenszyklus aktiv ist auch wenn sein UI-Status nicht geändert werden kann.
  • Jede Klasse, die ein ähnliches Verhalten wie die LiveData muss die Problemumgehung implementieren, die von Lifecycle Version beta 2 und niedriger.

Weitere Informationen

Weitere Informationen zum Umgang mit Lebenszyklen mit lebenszyklusbezogenen Komponenten erhalten Sie in den folgenden zusätzlichen Ressourcen.

Produktproben

Codelabs

Blogs