Kotlin-Koroutinen bieten eine API, mit der Sie schreiben können,
asynchronem Code. Mit Kotlin-Koroutinen können Sie
CoroutineScope
,
was Ihnen hilft, zu steuern, wann Ihre gemeinsamen Routinen ausgeführt werden sollen. Jede asynchrone
in einem bestimmten Bereich ausgeführt wird.
Lebenszyklusbewusste Komponenten bieten
Erstklassige Unterstützung von Koroutinen für logische Bereiche in Ihrer App sowie ein
Interoperabilitätsebene mit LiveData
.
In diesem Thema wird erläutert, wie Sie Koroutinen effektiv mit Lifecycle-Aware nutzen können.
Komponenten.
KTX-Abhängigkeiten hinzufügen
Die in diesem Thema beschriebenen integrierten Koroutinen sind in der KTX-Erweiterungen für die jeweilige Komponente. Vergessen Sie nicht, bei der Verwendung dieser Bereiche.
- Verwenden Sie für
ViewModelScope
androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0
oder höher. - Verwenden Sie für
LifecycleScope
androidx.lifecycle:lifecycle-runtime-ktx:2.4.0
oder höher. - Verwenden Sie für
liveData
androidx.lifecycle:lifecycle-livedata-ktx:2.4.0
oder höher.
Lebenszyklusbewusste Koroutinenbereiche
Lebenszyklusbewusste Komponenten definieren die folgenden integrierten Bereiche, die Sie verwenden können in Ihrer App.
Modellumfang anzeigen
Für jede ist ein ViewModelScope
definiert.
ViewModel
in Ihrer App. Beliebig
In diesem Bereich gestartete Koroutine wird automatisch abgebrochen, wenn die ViewModel
ist gelöscht. Koroutinen sind hier hilfreich, wenn Sie bei Ihrer Arbeit
wird nur ausgeführt, wenn ViewModel
aktiv ist. Wenn Sie zum Beispiel einige
Daten für ein Layout verwenden, sollten Sie die Arbeit auf ViewModel
festlegen, sodass, wenn das
ViewModel
ist gelöscht. Die Arbeit wird automatisch abgebrochen, um sie nicht weiter zu verarbeiten.
Ressourcen.
Auf den CoroutineScope
eines ViewModel
können Sie über die
viewModelScope
-Eigenschaft von ViewModel, wie im folgenden Beispiel gezeigt:
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}
Lebenszyklusbereich
Für jede ist ein LifecycleScope
definiert.
Objekt Lifecycle
. Beliebige Koroutine
in diesem Bereich gestartet, wird abgebrochen, wenn Lifecycle
gelöscht wird. Sie können
Zugriff auf CoroutineScope
von Lifecycle
entweder über
lifecycle.coroutineScope
- oder lifecycleOwner.lifecycleScope
-Property.
Das folgende Beispiel zeigt, wie lifecycleOwner.lifecycleScope
verwendet wird, um
Vorberechneten Text asynchron erstellen:
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(longTextContent, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
}
Neustartfähige Lebenszyklus-orientierte Koroutinen
Auch wenn lifecycleScope
eine ordnungsgemäße Stornierungsmöglichkeit darstellt,
lang andauernde Vorgänge automatisch ausführen, wenn Lifecycle
den Wert DESTROYED
hat,
gibt es vielleicht Fälle, in denen Sie mit der Ausführung
blockieren, wenn Lifecycle
sich in einem bestimmten Status befindet, und abbrechen, wenn er sich in einem
in einen anderen Bundesstaat ein. Sie können beispielsweise einen Ablauf erfassen, wenn der
Lifecycle
hat den Status „STARTED
“ und bricht die Sammlung bei STOPPED
ab. Dieses
die Strömungsemissionen nur dann verarbeitet,
wenn die Benutzeroberfläche auf dem Bildschirm sichtbar ist.
um Ressourcen zu sparen und App-Abstürze zu vermeiden.
In diesen Fällen geben Lifecycle
und LifecycleOwner
die Sperre an
repeatOnLifecycle
-API, die genau das tut. Das folgende Beispiel enthält eine
Codeblock, der jedes Mal ausgeführt wird, wenn sich die zugehörige Lifecycle
mindestens im
STARTED
und wird abgebrochen, wenn Lifecycle
den Status STOPPED
hat:
class MyFragment : Fragment() {
val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Create a new coroutine in the lifecycleScope
viewLifecycleOwner.lifecycleScope.launch {
// repeatOnLifecycle launches the block in a new coroutine every time the
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// Trigger the flow and start listening for values.
// This happens when lifecycle is STARTED and stops
// collecting when the lifecycle is STOPPED
viewModel.someDataFlow.collect {
// Process item
}
}
}
}
}
Lebenszyklusbasierte Erfassung von Abläufen
Wenn Sie eine Lebenszyklusanalyse nur für einen einzigen Ablauf durchführen müssen, können Sie
verwenden Sie die
Flow.flowWithLifecycle()
zur Vereinfachung Ihres Codes:
viewLifecycleOwner.lifecycleScope.launch {
exampleProvider.exampleFlow()
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.collect {
// Process the value.
}
}
Wenn Sie jedoch eine
lebenszyklusbezogene Erfassung für mehrere Abläufe in
müssen Sie jeden Ablauf in verschiedenen Koroutinen erfassen. In diesem Fall
ist es effizienter, repeatOnLifecycle()
direkt zu verwenden:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// Because collect is a suspend function, if you want to
// collect multiple flows in parallel, you need to do so in
// different coroutines.
launch {
flow1.collect { /* Process the value. */ }
}
launch {
flow2.collect { /* Process the value. */ }
}
}
}
Lebenszyklusbewusste Koroutinen aussetzen
Auch wenn CoroutineScope
eine ordnungsgemäße Stornierungsmöglichkeit darstellt,
Vorgänge mit langer Ausführungszeit automatisch ausführen,
die Ausführung eines Codeblocks aussetzen, es sei denn, das Lifecycle
befindet sich in einem bestimmten
Bundesstaat. Wenn Sie beispielsweise einen FragmentTransaction
ausführen möchten, müssen Sie warten, bis der
Lifecycle
ist mindestens STARTED
. In diesen Fällen bietet Lifecycle
zusätzliche Methoden: lifecycle.whenCreated
, lifecycle.whenStarted
und
lifecycle.whenResumed
. Jede gemeinsame Ausführung innerhalb dieser Blöcke wird ausgesetzt, wenn
Lifecycle
ist nicht wenigstens im gewünschten minimalen Zustand.
Das Beispiel unten enthält einen Codeblock, der nur ausgeführt wird, wenn die zugehörigen
Lifecycle
hat mindestens den Status STARTED
:
class MyFragment: Fragment {
init { // Notice that we can safely launch in the constructor of the Fragment.
lifecycleScope.launch {
whenStarted {
// The block inside will run only when Lifecycle is at least STARTED.
// It will start executing when fragment is started and
// can call other suspend methods.
loadingView.visibility = View.VISIBLE
val canAccess = withContext(Dispatchers.IO) {
checkUserAccess()
}
// When checkUserAccess returns, the next line is automatically
// suspended if the Lifecycle is not *at least* STARTED.
// We could safely run fragment transactions because we know the
// code won't run unless the lifecycle is at least STARTED.
loadingView.visibility = View.GONE
if (canAccess == false) {
findNavController().popBackStack()
} else {
showContent()
}
}
// This line runs only after the whenStarted block above has completed.
}
}
}
Wenn die Lifecycle
gelöscht wird, während eine Koroutine über eine der
when
Methoden verwenden, wird die Koroutine automatisch abgebrochen. Im Beispiel unten
Der finally
-Block wird ausgeführt, sobald der Lifecycle
-Status DESTROYED
ist:
class MyFragment: Fragment {
init {
lifecycleScope.launchWhenStarted {
try {
// Call some suspend functions.
} finally {
// This line might execute after Lifecycle is DESTROYED.
if (lifecycle.state >= STARTED) {
// Here, since we've checked, it is safe to run any
// Fragment transactions.
}
}
}
}
}
Koroutinen mit LiveData verwenden
Wenn Sie LiveData
verwenden, benötigen Sie
um Werte asynchron zu berechnen. Sie können z. B. ein
und stellen sie auf Ihrer Benutzeroberfläche bereit. In
In diesen Fällen können Sie mit der Builder-Funktion liveData
ein suspend
-Objekt aufrufen.
und liefert das Ergebnis als LiveData
-Objekt.
Im folgenden Beispiel ist loadUser()
eine an anderer Stelle deklarierte Sperrfunktion. Verwenden Sie
die Builder-Funktion liveData
, um loadUser()
asynchron aufzurufen, und
Verwenden Sie emit()
, um das Ergebnis auszugeben:
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
Der Baustein liveData
dient als
strukturiertes Primitiv für Nebenläufigkeit
zwischen Koroutinen und LiveData
. Die Ausführung des Codeblocks beginnt, wenn
LiveData
wird aktiv und automatisch abgebrochen, nachdem ein
Zeitlimit, wenn LiveData
inaktiv wird. Wenn es vor dem
abgeschlossen wird, wird er neu gestartet, wenn LiveData
wieder aktiv wird. Wenn es
bei einer vorherigen Ausführung erfolgreich abgeschlossen wurde, wird er nicht neu gestartet. Beachten Sie, dass dies
neu gestartet, wenn der Vorgang automatisch abgebrochen wird. Wird die Blockierung für eine andere
(z.B. durch Auslösen eines CancellationException
), wird er nicht neu gestartet.
Sie können auch mehrere Werte aus dem Block ausgeben. Jeder emit()
-Anruf wird unterbrochen
der Ausführung des Blocks, bis der Wert LiveData
im Hauptthread festgelegt ist.
val user: LiveData<Result> = liveData {
emit(Result.loading())
try {
emit(Result.success(fetchUser()))
} catch(ioException: Exception) {
emit(Result.error(ioException))
}
}
Sie können auch liveData
kombinieren mit
Transformations
, wie in
im folgenden Beispiel:
class MyViewModel: ViewModel() {
private val userId: LiveData<String> = MutableLiveData()
val user = userId.switchMap { id ->
liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) {
emit(database.loadUserById(id))
}
}
}
Sie können mehrere Werte aus einem LiveData
ausgeben, indem Sie die emitSource()
aufrufen.
wann immer Sie einen neuen Wert ausgeben möchten. Jeder Aufruf von emit()
oder emitSource()
entfernt die zuvor hinzugefügte Quelle.
class UserDao: Dao {
@Query("SELECT * FROM User WHERE id = :id")
fun getUser(id: String): LiveData<User>
}
class MyRepository {
fun getUser(id: String) = liveData<User> {
val disposable = emitSource(
userDao.getUser(id).map {
Result.loading(it)
}
)
try {
val user = webservice.fetchUser(id)
// Stop the previous emission to avoid dispatching the updated user
// as `loading`.
disposable.dispose()
// Update the database.
userDao.insert(user)
// Re-establish the emission with success type.
emitSource(
userDao.getUser(id).map {
Result.success(it)
}
)
} catch(exception: IOException) {
// Any call to `emit` disposes the previous one automatically so we don't
// need to dispose it here as we didn't get an updated value.
emitSource(
userDao.getUser(id).map {
Result.error(exception, it)
}
)
}
}
}
Weitere Informationen zu Koroutinen finden Sie unter den folgenden Links:
Weitere Informationen
Weitere Informationen zur Verwendung von Koroutinen mit lebenszyklusbezogenen Komponenten finden Sie unter die folgenden zusätzlichen Ressourcen.
Produktproben
Blogs
- <ph type="x-smartling-placeholder"></ph> Koroutinen unter Android: Anwendungsmuster
- <ph type="x-smartling-placeholder"></ph> Einfache Coroutinen in Android: viewModelScope
- <ph type="x-smartling-placeholder"></ph> Zwei aufeinanderfolgende LiveData-Emissionen in gleichzeitigen Routinen testen
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- LiveData-Übersicht
- Lebenszyklen mit lebenszyklusbezogenen Komponenten verarbeiten
- Daten aus Seiten laden und anzeigen