Verwenden von RememberCoroutineScope() vs. LaunchedEffect

Lesezeit: 7 Minuten

Benutzeravatar von machfour
machvier

Kontext

In Jetpack compose haben wir die Möglichkeit, zu verwenden rememberCoroutineScope() sowie die Verwendung der LaunchedEffect composable, um Coroutinen zu verwenden / Suspend-Funktionen auszuführen (Snackbars anzeigen usw.).

Die Konvention, die ich bisher übernommen habe, besteht darin, sich einen einzelnen Coroutine-Bereich oben in meinem Kompositionsbaum zu merken und ihn über Funktionsargumente an Stellen weiterzugeben, an denen er benötigt wird. Dies scheint vage eine gute Praxis zu sein, aber auf der anderen Seite fügt es meinen Funktionssignaturen zusätzliches Rauschen hinzu.

Fragen

  1. Gibt es Gründe für die bevorzugte Verwendung von LaunchedEffect Über rememberCoroutineScope() in zusammensetzbaren Funktionen?
  2. Lohnt es sich, nur einmal pro Compose-Baum einen Coroutine-Bereich zu erstellen / zu speichern, oder sollte ich einfach anrufen? rememberCoroutineScope() in jeder Funktion, wo tatsächlich eine Coroutine gestartet wird?

  • Ich weiß, dass Nr. 1 kürzlich auf Kotlinlang Slack diskutiert wurde, aber die Suche von Slack ist erbärmlich, also kann ich es nicht finden. Für Nr. 2 ist das Problem nicht “die Mühe wert”, sondern was die richtige Antwort für die Coroutine ist. Du schreibst zum Beispiel AboutScreen() und habe da drin a Text() die die Anzahl der Sekunden seit dem Erstellen der App anzeigt, und Sie verwenden eine Coroutine, um den Status dafür zu aktualisieren Text(). Der Benutzer besucht den About-Bildschirm, verlässt ihn dann und verwendet andere Teile Ihrer App für eine Stunde.

    – CommonsWare

    4. März 2021 um 12:33 Uhr

  • Sollte Ihre jede Sekunde dauernde Coroutine die ganze Stunde laufen, obwohl der About-Bildschirm nicht in Ihrer Komposition enthalten ist? Wenn die Antwort “Heck, nein” lautet, dann brauchen Sie a CoroutineScope das ist auf das relevante Composable beschränkt, und das wäre rememberCoroutineScope() in diesem Composable. Wenn Sie aus irgendeinem Grund tun Wenn Sie diese Coroutine benötigen, um weiter ausgeführt zu werden, benötigen Sie einen Bereich, der der gewünschten Lebensdauer entspricht. Dies ist nicht anders als jede andere Entscheidung über den Gültigkeitsbereich von Coroutinen: Es geht nicht darum, was einfach ist, sondern um die richtige Lebensdauer für die Ausführung der Coroutine.

    – CommonsWare

    4. März 2021 um 12:35 Uhr

  • Danke, ich werde versuchen, mir Kotlinlang Slack anzusehen. Für Nr. 2 haben alle Bildschirme/Fragmente in meiner App separate Compose-Bäume, sodass die Lebensdauer höchstens so lang ist, wie ein Benutzer auf einem Bildschirm verbringt. Aber ich dachte mehr an kurzlebige Dinge, wie das Zeigen von Snackbars und Laufanimationen. Sollte ich denselben Koroutinenbereich für viele verschiedene kurzlebige Dinge wiederverwenden oder jedes Mal einen neuen erstellen? Es hört sich so an, als ob Sie sagen, dass ich die Bereiche nicht größer als nötig machen sollte, was dazu führt, dass die Coroutine-Bereiche an jedem Ort, an dem sie verwendet werden, separat erstellt / gespeichert werden.

    – machvier

    5. März 2021 um 1:22 Uhr

  • „Ich sollte die Bereiche nicht größer als nötig machen“ – im Rahmen des Zumutbaren, ja. Zurück zu Ihrer Frage: “Lohnt es sich, einen Koroutinenbereich nur einmal pro Erstellungsbaum zu erstellen/zu speichern”, lautet die Antwort “Nein”. Coroutine-Bereiche sind billig, und sich an Dinge zu erinnern ist billig, also besteht keine Notwendigkeit, ihre Verwendung künstlich einzuschränken. Sie können sich stattdessen darauf konzentrieren, was die Geschäftslogik für ihre Lebensdauer vorschreibt.

    – CommonsWare

    5. März 2021 um 12:29 Uhr

  • proandroiddev.com/… – Blogbeitrag zu LaunchedEffect und RememberCoroutineScope

    – Udit

    9. September 2021 um 9:01 Uhr


Benutzeravatar von nglauber
Englauber

Lassen Sie mein Verständnis hier:

Frage 1:
LaunchedEffect sollte verwendet werden, wenn Sie möchten, dass eine Aktion ausgeführt werden muss, wenn Ihr Composable zum ersten Mal gestartet/neu gestartet wird (oder wenn sich der Schlüsselparameter geändert hat). Wenn Sie beispielsweise einige Daten von Ihrem ViewModel anfordern oder eine Art Animation ausführen möchten …
rememberCoroutineScope Auf der anderen Seite ist es spezifisch, den Coroutine-Bereich zu speichern, sodass der Code einige starten kann suspend Funktion … imho, die einzige Beziehung zwischen ihnen ist, dass Sie auch a verwenden können LaunchedEffect um eine Coroutine zu starten…

Frage 2: Wie Sie in den Dokumenten sehen können, rememberCoroutineScope behält die Referenz des Geltungsbereichs der Coroutine an einem bestimmten Punkt der Komposition bei. Wenn daher ein bestimmtes Composable aus der Recomposition entfernt wird, wird diese Coroutine automatisch abgebrochen. Sie haben beispielsweise die folgenden zusammensetzbaren Aufrufe A -> B -> C. Wenn Sie sich an den Koroutinenbereich in erinnern C und es aus der Zusammensetzung entfernt wird, wird die Coroutine automatisch abgebrochen. Aber wenn Sie sich erinnern von Aführen Sie das Zielfernrohr durch B und Cverwenden Sie diesen Bereich in Cund dann C entfernt wird, läuft die Coroutine weiter (weil sie in gespeichert wurde A)…

  • Nachtrag: Ich verwende AS Arctic Fox Canary 12 und sehe jetzt explizite Warnungen vor der Verwendung coroutineScope.launch { ... } innerhalb einer zusammensetzbaren Funktion: “Aufrufe zum Starten sollten innerhalb eines LaunchedEffect und nicht in einer Komposition erfolgen.” (Der Lint-Name lautet „CoroutineCreationDuringComposition“). Also für das, was es wert ist, gibt dies eine “offizielle” Anweisung, dass meine vorherige Art, einen coroutineScope für alles zu verwenden, nicht die bevorzugte Methode war.

    – machvier

    27. März 2021 um 11:20 Uhr

  • RememberCoroutineScope dient zum Starten einer Coroutine außerhalb eines Composable innerhalb eines kompositionsbewussten Bereichs. Für innerhalb von Composables sollten Coroutinen mit LaunchedEffect gestartet werden

    – Dissident Dev

    28. Dezember 2021 um 7:31 Uhr

Verwenden Sie RememberCoroutineScope() wenn Sie Coroutinen verwenden und die Coroutine nach einem Ereignis abbrechen und neu starten müssen

Verwenden Sie LaunchedEffect() wenn Sie Coroutinen verwenden und die Coroutine jedes Mal abbrechen und neu starten müssen, wenn sich Ihr Parameter ändert und er nicht in einem änderbaren Zustand gespeichert ist.

LaunchedEffect: Führen Sie Suspend-Funktionen im Bereich eines Composable aus

Verwenden Sie zum sicheren Aufrufen von Suspend-Funktionen aus einem Composable das LaunchedEffect Composable. Wenn LaunchedEffect in die Komposition eintritt, startet es eine Coroutine mit dem als Parameter übergebenen Codeblock. Die Coroutine wird abgebrochen, wenn LaunchedEffect die Komposition verlässt.

RememberCoroutineScope: Abrufen eines kompositionsbewussten Bereichs zum Starten einer Coroutine außerhalb eines Composable

Da LaunchedEffect eine zusammensetzbare Funktion ist, kann sie nur innerhalb anderer zusammensetzbarer Funktionen verwendet werden. Um eine Coroutine außerhalb eines zusammensetzbaren Objekts zu starten, aber so festgelegt, dass sie automatisch abgebrochen wird, sobald sie die Zusammensetzung verlässt, verwenden Sie RememberCoroutineScope

Mehr von hier

rememberCoroutineScope ist eine zusammensetzbare Funktion, die einen CoroutineScope zurückgibt, der an den Punkt der Komposition gebunden ist, an dem er aufgerufen wird. Der Umfang wird abgebrochen, wenn der Anruf die Zusammensetzung verlässt. Wenn Sie Ihren eigenen coroutineScope erstellen, anstatt sich daran zu erinnern, erhalten Sie möglicherweise MonotonicFrameClock is not available in this CoroutineContext Fehler wie hier in Frage.

LaunchedEffect ist ein Erinnern unter der Haube mit coroutineScope, das ausgeführt wird, wenn es in die Komposition eintritt und wenn sich einer seiner Schlüssel ändert.

@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
    key1: Any?,
    block: suspend CoroutineScope.() -> Unit
) {
    val applyContext = currentComposer.applyCoroutineContext
    remember(key1) { LaunchedEffectImpl(applyContext, block) }
}

LaunchedEffect eignet sich gut zum Starten von Animationen, Snackbars, Scrolls, jeder standardmäßigen Suspendierungsfunktion. Compose hat auch eine weitere sehr nützliche Verwendung, nämlich das Auslösen einiger Ereignisse ohne Benutzerinteraktion

Beispielsweise wird ein Rückruf nur ausgeführt, wenn ein bestimmter Zustand ohne Benutzerinteraktionen wie in dieser Frage erreicht wird

LaunchedEffect(statementsByYear != null) {

   if (statementsByYear != null) {
         onDataAcquired(statementsByYear!!) 
   } 
}

LaunchedEffect wird bei der Komposition ausgelöst, aber wie fraglich, da der Wert null ist, wenn die Blockbedingung dann nicht erfüllt ist statementsByYear wird von api gesetzt statementsByYear != null von true auf false wechselt und wenn die Blockbedingung erfüllt ist und Sie die Anweisung ausführen.

Oder nur einmal in einem Composable mit Hilfe von ViewModel, das ein Flag wie in dieser Frage speichert.

Der Block von LaunchedEffect(keys) wird beim Komponieren aufgerufen und wenn sich irgendwelche Keys ändern. Wenn Sie Schlüssel von Ihrem ViewModel festlegen, wird dieser LaunchedEffect gestartet und Sie können einen bedingten Block erstellen, der dasselbe Flag auf wahr prüft, das in ViewModel enthalten ist

LaunchedEffect(mViewModel.isLaunched) {
    if(!mViewModel.isLaunched) {
          mViewMode.iniBilling(context as Activity)
          mViewMode.isLaunched = true
    }
}

Auch bedingte Blöcke treten in die Komposition ein, wenn die Bedingungen erfüllt sind. Wenn Sie also LaunchedEffect mit if-Block umschließen, können Sie definieren, wann er in die Komposition eintritt und ihn verlässt, was bedeutet, dass der Auftrag abgebrochen wird, dessen coroutineScope möglicherweise wie in dieser Antwort ausgeführt wird

if (count > 0 && count <5) {
    // `LaunchedEffect` will cancel and re-launch if
    // `scaffoldState.snackbarHostState` changes
    LaunchedEffect(scaffoldState.snackbarHostState) {
        // Show snackbar using a coroutine, when the coroutine is cancelled the
        // snackbar will automatically dismiss. This coroutine will cancel whenever
        // if statement is false, and only start when statement is true
        // (due to the above if-check), or if `scaffoldState.snackbarHostState` changes.
        scaffoldState.snackbarHostState.showSnackbar("count $count")
    }
}

Dieser Block tritt in die Zusammensetzung ein, wenn zählen größer als 0 ist und in der Zusammensetzung bleibt, während count kleiner als 5 ist, aber da es sich um einen LaunchedEffect handelt, wird er einmal ausgelöst, aber wenn die Anzahl 5 schneller erreicht als die Snackbar-Dauer, wird die Snackbar abgebrochen, da der Block die Zusammensetzung verlässt.

1436350cookie-checkVerwenden von RememberCoroutineScope() vs. LaunchedEffect

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy