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, aLiveData
nie. LiveData.observe()
automatycznie wyrejestrowuje konsumenta, gdy przechodzi do stanuSTOPPED
, a zbieranie zStateFlow
lub inne procesy nie przestają gromadzić automatycznie danych. Aby osiągnąć taki sam , musisz zbierać przepływy zLifecycle.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ą jestBufferOverflow.SUSPEND
. co powoduje zawieszenie połączenia. Inne opcje toDROP_LATEST
lubDROP_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
- Procedura Kotlin w Androidzie
- Testowanie procesów Kotlin na Androidzie
- Co warto wiedzieć o operatorachshareIn i stateIn Flow
- Migracja z LiveData do Kotlin Flow
- Dodatkowe materiały na temat współrzędnych i przepływu Kotlin