Wprowadzenie do przepływów pracy

Wersja interfejsów API Home w Kotlinie korzysta z przepływów, czyli funkcji języka Kotlin, która zapewnia zaawansowane możliwości obsługi asynchronicznych strumieni danych, w tym przekształcania, filtrowania, mapowania, konwertowania na kolekcje i z nich itp.

Coroutines są ściśle powiązane z przepływami i stanowią elegancką abstrakcję do pisania kodu asynchronicznego. Dzięki nim deweloper nie musi już jawnie pisać procedur wywołania. Przepływy współpracują z korowinatami, umożliwiając asynchroniczne pobieranie danych z nich bez konieczności czekania na zakończenie funkcji lub wątku.

Dane są pobierane z przepływu przez konsumenta w ramach procesu o nazwie zbieranie. Przepływy w Kotlinie mają funkcję collect(), która może być używana do tego celu. To, co dzieje się w procesie podczas zbierania danych, zależy od jego typu.

Domyślnie procesy Kotlina (Flow<T>) są zimne, co oznacza, że kod kreatora procesów generuje dane tylko wtedy, gdy wywoływana jest funkcja collect(). Natomiast gorący przepływ danych natychmiast generuje dane, buforując je w pamięci jako kolejkę, z której dane są pobierane. Ponieważ przechowuje dane w pamięci, gorący przepływ jest uważany za stanowy.

Za pomocą operatora shareIn możesz przekształcić ścieżki z zimnych w ciepłe (zobacz Zmienianie zimnych ścieżek w ciepłe za pomocą operatora shareIn). Możesz też użyć operatora SharedFlow lub StateFlow, aby przekształcić zimną ścieżkę w ciepłą.

Jak przykładowa aplikacja korzysta z przepływów

Przykładowa aplikacja korzysta z modeli widoku w Jetpack Compose połączonych z przepływami w interfejsach API Home. Wiele elementów interfejsu w próbnej aplikacji jest zależnych od stanu, ale można z nimi wchodzić w interakcje. Przykładowa aplikacja łączy stany urządzeń z „optymistycznym” stanem w interfejsie, aby zapewnić użytkownikom wrażenia w czasie rzeczywistym. Termin „optymistyczny” oznacza, że aplikacja zakłada, że dane działanie zakończyło się powodzeniem, i natychmiast aktualizuje interfejs, aby odzwierciedlić oczekiwany wynik, bez oczekiwania na potwierdzenie. Jeśli okaże się, że działanie się nie powiodło, interfejs użytkownika zostanie zaktualizowany, aby odzwierciedlić rzeczywisty stan.

W przykładowej aplikacji przepływy są tworzone dla każdej warstwy modelu widoku (struktury, pomieszczenia, urządzenia). Na przykład w przypadku struktur z tym wywołaniem w GlobalViewModel.kt:

  fun getStructuresState(): StateFlow<List<StructureModel>?> =
    homeClient
      .structures()
      .map { structures -> structures.map { StructureModel(it.name, it) }.sortedBy { it.name } }
      .handleErrors()
      .flowOn(Dispatchers.IO)
      .stateIn(scope = viewModelScope, started = SharingStarted.WhileSubscribed(), null)

emitAll() zbiera wszystkie wartości z danego przepływu i wysyła je do odbiornika. stateIn() udostępnia ostatnio wyemitowaną wartość z pojedynczej instancji przepływu w górę z wieloma subskrybentami w dół. Więcej informacji znajdziesz w dokumentacji kotlinx.coroutines.flow.

Jetpack Compose

Aby przechowywać obiekty przepływu w pamięci lokalnej i zapobiegać ich zakończeniu, użyj interfejsu API Kotlina remember.

Jeśli w Jetpack Compose korzystasz z elementu collectAsStateWithLifecycle(), Jetpack automatycznie zarządza subskrypcjami i rezygnacją z nich w zależności od tego, czy interfejs użytkownika aplikacji, który pokazuje ten stan, znajduje się na pierwszym planie, czy nie.

Wystarczy wywołać go w przykładowej aplikacji. Korzystając z funkcji getStructuresState(), która została wcześniej pokazana:

val structuresList by
    remember(globalViewModel) { globalViewModel.getStructuresState() }.collectAsStateWithLifecycle()

Teraz, gdy zmieni się jakikolwiek stan struktury (np. name), funkcja składana, która z niego korzysta, automatycznie odzwierciedli tę zmianę. W przykładowej aplikacji jest to funkcja HomeActivityContent().

Zasoby

Więcej informacji o Kotlin, przepływach, współbieżnych wątkach i Jetpack Compose znajdziesz w tych materiałach: