StateFlow i SharedFlow

StateFlow i SharedFlow to interfejsy Flow API które umożliwiają przepływom optymalnie emitowanie aktualizacji stanu i wysyłanie wartości do wielu konsumentów.

StateFlow

StateFlow to obserwowany przepływ posiadacza stanu, który emituje stan bieżący i nowy dla jej kolektorów. Wartość bieżącego stanu można również odczytać value usłudze. Aby zaktualizować stan i wysłać go do przepływu, przypisz nową wartość do właściwości value elementu Zajęcia: MutableStateFlow.

Na Androidzie StateFlow doskonale nadaje się do zajęć, które wymagają możliwy do obserwacji zmienny stan.

Korzystając z przykładów z przepływów Kotlin, StateFlow może być ujawniony z LatestNewsViewModel, tak aby View mógł nasłuchuj aktualizacji stanu UI i zapewniaj przetrwanie stanu ekranu. zmian konfiguracji.

class LatestNewsViewModel(
    private val newsRepository: NewsRepository
) : ViewModel() {

    // Backing property to avoid state updates from other classes
    private val _uiState = MutableStateFlow(LatestNewsUiState.Success(emptyList()))
    // The UI collects from this StateFlow to get its state updates
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    init {
        viewModelScope.launch {
            newsRepository.favoriteLatestNews
                // Update View with the latest favorite news
                // Writes to the value property of MutableStateFlow,
                // adding a new element to the flow and updating all
                // of its collectors
                .collect { favoriteNews ->
                    _uiState.value = LatestNewsUiState.Success(favoriteNews)
                }
        }
    }
}

// Represents different states for the LatestNews screen
sealed class LatestNewsUiState {
    data class Success(val news: List<ArticleHeadline>): LatestNewsUiState()
    data class Error(val exception: Throwable): LatestNewsUiState()
}

Klasa odpowiedzialna za aktualizację obiektu MutableStateFlow to producent, a wszystkie klasy zbierane z biblioteki StateFlow są konsumentów. Nie podoba mi się przepływu zimnego utworzonego za pomocą konstruktora flow, StateFlow jest gorące: nie powoduje uruchomienia żadnego kodu producenta. StateFlow jest zawsze aktywny i w pamięci, przez co kwalifikuje się do czyszczenia pamięci kolekcji tylko wtedy, gdy nie ma innych odwołań do niej z kosza katalog główny kolekcji.

Gdy nowy konsument rozpocznie zbieranie danych z procesu, otrzyma ostatni w strumieniu i wszystkich kolejnych stanach. Wykrycie tego zachowania w innych dostrzegalnych klasach, takich jak LiveData

Element View nasłuchuje StateFlow, tak jak w przypadku każdego innego przepływu:

class LatestNewsActivity : AppCompatActivity() {
    private val latestNewsViewModel = // getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Start a coroutine in the lifecycle scope
        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.
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Trigger the flow and start listening for values.
                // Note that this happens when lifecycle is STARTED and stops
                // collecting when the lifecycle is STOPPED
                latestNewsViewModel.uiState.collect { uiState ->
                    // New value received
                    when (uiState) {
                        is LatestNewsUiState.Success -> showFavoriteNews(uiState.news)
                        is LatestNewsUiState.Error -> showError(uiState.exception)
                    }
                }
            }
        }
    }
}
.

Aby przekonwertować dowolny przepływ na StateFlow, użyj funkcji stateIn. pośrednim.

StateFlow, Flow i LiveData

StateFlow i LiveData mają podobieństw. Obie są obserwowalnymi klasami właścicieli danych i obydwie są zgodne podobnego wzorca w architekturze aplikacji.

Pamiętaj jednak, że StateFlow oraz LiveData działa inaczej:

  • Funkcja StateFlow wymaga przekazania do konstruktora stanu początkowego, a LiveData nie.
  • LiveData.observe() automatycznie wyrejestrowuje konsumenta, gdy przechodzi do stanu STOPPED, a zbieranie z StateFlow lub inne procesy nie przestają gromadzić automatycznie danych. Aby osiągnąć taki sam , musisz zbierać przepływy z Lifecycle.repeatOnLifecycle blokować.

Ogrzewanie zimnych przepływów za pomocą shareIn

StateFlow jest przepływem gorącym – pozostaje w pamięci, dopóki jest nie jest zebrana lub gdy istnieją inne odwołania do niej z usługi czyszczenia pamięci pierwiastek. Aby przełączyć przepływy „na zimno” na ciepło, za pomocą shareIn .

Użycie obiektu callbackFlow utworzonego w przepływach Kotlin jako Możesz na przykład zamiast tworzyć nowy przepływ przez każdy kolektor, dane pobrane z Firestore między kolektorami przy użyciu shareIn. Musisz przekazać następujące dokumenty:

  • Element CoroutineScope używany do udostępniania przepływu pracy. Ten zakres powinien być aktywny aby proces był aktywny tak długo, jak to konieczne.
  • Liczba elementów do ponownego odtworzenia każdemu nowemu kolekcjonerowi.
  • Zasada działania uruchamiania.
class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed()
    )
}

W tym przykładzie proces latestNews ponownie odtwarza ostatni wyemitowany element do nowego kolektora i pozostanie aktywny, dopóki externalScope będzie ale mamy też aktywne kolektory. SharingStarted.WhileSubscribed() zasada uruchamiania sprawia, że producent nadrzędny jest aktywny, dopóki są aktywne subskrybentów. Dostępne są też inne zasady uruchamiania, takie jak SharingStarted.Eagerly, aby natychmiast uruchomić producenta, lub SharingStarted.Lazily, aby rozpocząć udostępnianie po pojawieniu się pierwszego subskrybenta i zadbaj o trwałość procesu.

SharedFlow

Funkcja shareIn zwraca SharedFlow, przepływ gorący, który generuje wartości wszystkim konsumentom, którzy z niej korzystają. SharedFlow to konfigurowalne uogólnienie funkcji StateFlow.

SharedFlow możesz utworzyć bez użycia metody shareIn. Możesz na przykład można użyć narzędzia SharedFlow do wysyłania znaczników do reszty aplikacji, wszystkie treści są okresowo odświeżane w tym samym czasie. Oprócz pobierania najnowszych wiadomości, możesz też odświeżyć okno sekcji informacyjnej, która zawiera kolekcję ulubionych tematów. W następujących fragment kodu, TickHandler ujawnia element SharedFlow, tak by inne muszą wiedzieć, kiedy odświeżyć treści. Tak jak w przypadku strony StateFlow, użyj parametru właściwość Backing typu MutableSharedFlow w klasie służącej do wysyłania elementów w toku:

// Class that centralizes when the content of the app needs to be refreshed
class TickHandler(
    private val externalScope: CoroutineScope,
    private val tickIntervalMs: Long = 5000
) {
    // Backing property to avoid flow emissions from other classes
    private val _tickFlow = MutableSharedFlow<Unit>(replay = 0)
    val tickFlow: SharedFlow<Event<String>> = _tickFlow

    init {
        externalScope.launch {
            while(true) {
                _tickFlow.emit(Unit)
                delay(tickIntervalMs)
            }
        }
    }
}

class NewsRepository(
    ...,
    private val tickHandler: TickHandler,
    private val externalScope: CoroutineScope
) {
    init {
        externalScope.launch {
            // Listen for tick updates
            tickHandler.tickFlow.collect {
                refreshLatestNews()
            }
        }
    }

    suspend fun refreshLatestNews() { ... }
    ...
}

Działanie SharedFlow możesz dostosować na te sposoby:

  • replay pozwala ponownie wysłać pewną liczbę wcześniej wygenerowanych wartości dla nowych subskrybentów.
  • onBufferOverflow umożliwia określenie zasady, która określa, kiedy bufor W domenie znajduje się wiele elementów do wysłania. Wartością domyślną jest BufferOverflow.SUSPEND. co powoduje zawieszenie połączenia. Inne opcje to DROP_LATEST lub DROP_OLDEST

MutableSharedFlow ma też właściwość subscriptionCount, która zawiera liczbę aktywnych kolektorów, aby można było zoptymalizować działalność z odpowiednimi zasadami. MutableSharedFlow zawiera też resetReplayCache , jeśli nie chcesz ponownie odtwarzać najnowszych informacji wysłanych do procesu.

Dodatkowe materiały dotyczące przepływu