StateFlow
dan SharedFlow
adalah Flow API
yang memungkinkan alur memunculkan pembaruan status dan nilai secara optimal ke beberapa
konsumen.
StateFlow
StateFlow
adalah alur yang dapat diamati pemegang status yang akan memunculkan pembaruan status saat ini dan yang baru
kepada kolektornya. Nilai status saat ini juga dapat dibaca melalui properti value
. Untuk memperbarui status dan mengirimkannya ke alur, tetapkan nilai baru ke
properti value
dari
class MutableStateFlow
.
Di Android, StateFlow
sangat cocok untuk class yang perlu mempertahankan
status yang dapat diubah dan diamati.
Dengan mengikuti contoh dari alur Kotlin, StateFlow
dapat ditampilkan dari LatestNewsViewModel
sehingga View
dapat
mendeteksi pembaruan status UI dan secara inheren membuat status layar tidak terpengaruh
perubahan konfigurasi.
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()
}
Class yang bertanggung jawab untuk memperbarui MutableStateFlow
adalah produser,
dan semua class yang mengumpulkan dari StateFlow
adalah konsumen. Tidak seperti
alur dingin yang dibuat menggunakan builder flow
, StateFlow
bersifat panas:
pengumpulan dari alur tidak memicu kode produser apa pun. StateFlow
selalu aktif, berada dalam memori, dan valid untuk
pembersihan sampah memori hanya jika tidak ada referensi lain terhadapnya dari root
pembersihan sampah memori.
Saat konsumen baru mulai mengumpulkan dari flow, status terakhir dalam aliran data
dan status berikutnya akan diterima. Anda dapat menemukan perilaku ini
dalam class lain yang dapat diamati seperti
LiveData
.
View
akan memproses StateFlow
seperti pada alur lainnya:
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)
}
}
}
}
}
}
Untuk mengonversi flow apa pun menjadi StateFlow
, gunakan
operator perantara
stateIn
.
StateFlow, Alur, dan LiveData
StateFlow
dan LiveData
memiliki
kemiripan. Keduanya merupakan class pemegang data yang dapat diamati, dan mengikuti
pola serupa saat digunakan dalam arsitektur aplikasi.
Namun, perlu diperhatikan bahwa StateFlow
dan
LiveData
berperilaku berbeda:
StateFlow
memerlukan status awal untuk diteruskan ke konstruktor, sedangkanLiveData
tidak.LiveData.observe()
akan otomatis membatalkan pendaftaran konsumen saat tampilan beralih ke statusSTOPPED
, sedangkan pengumpulan dariStateFlow
atau alur lainnya tidak menghentikan pengumpulan secara otomatis. Untuk mencapai perilaku yang sama, Anda harus mengumpulkan alur dari blokLifecycle.repeatOnLifecycle
.
Membuat flow cold menjadi hot menggunakan shareIn
StateFlow
adalah flow hot, yang akan tetap berada di memori selama flow
tersebut dikumpulkan atau saat referensi lain terhadapnya tersedia dari root pembersihan sampah
memori. Anda dapat mengubah flow cold menjadi hot menggunakan operator
shareIn
.
Dengan menggunakan callbackFlow
yang dibuat dalam flow Kotlin sebagai
contoh, Anda dapat membagikan
data yang diambil dari Firestore di antara kolektor menggunakan shareIn
, bukan meminta setiap kolektor membuat flow baru.
Anda harus meneruskan hal berikut:
CoroutineScope
yang digunakan untuk membagikan alur. Cakupan ini harus lebih tahan lama dari konsumen mana pun agar alur bersama tetap aktif selama diperlukan.- Jumlah item yang akan diputar ulang ke setiap kolektor baru.
- Kebijakan perilaku mulai.
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
Dalam contoh ini, alur latestNews
akan memutar ulang item yang terakhir dimunculkan
ke kolektor baru dan akan tetap aktif selama externalScope
masih aktif dan terdapat kolektor yang aktif. Kebijakan mulai SharingStarted.WhileSubscribed()
akan membuat produser upstream tetap aktif meskipun terdapat
subscriber yang aktif. Kebijakan mulai lainnya tersedia, misalnya
SharingStarted.Eagerly
untuk segera memulai produser atau
SharingStarted.Lazily
untuk mulai berbagi setelah pelanggan pertama muncul
dan membuat flow terus aktif.
SharedFlow
Fungsi shareIn
akan menampilkan SharedFlow
, yakni alur panas yang memunculkan nilai
ke semua konsumen yang mengumpulkan darinya. A SharedFlow
adalah
generalisasi StateFlow
yang sangat dapat dikonfigurasi.
Anda dapat membuat SharedFlow
tanpa menggunakan shareIn
. Misalnya, Anda
dapat menggunakan SharedFlow
untuk mengirim tick ke bagian lain aplikasi agar
semua konten di-refresh secara berkala pada waktu yang sama. Selain
mengambil berita terbaru, Anda juga dapat me-refresh bagian
informasi pengguna dengan koleksi topik favoritnya. Dalam cuplikan
kode berikut, TickHandler
menampilkan SharedFlow
sehingga class lainnya
tahu kapan harus me-refresh kontennya. Seperti halnya StateFlow
, gunakan
properti pendukung jenis MutableSharedFlow
dalam class untuk mengirim item
ke alur:
// 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() { ... }
...
}
Anda dapat menyesuaikan perilaku SharedFlow
dengan cara berikut:
replay
memungkinkan Anda mengirim ulang sejumlah nilai yang sebelumnya dimunculkan untuk subscriber baru.onBufferOverflow
memungkinkan Anda menentukan kebijakan tentang kapan buffer penuh berisi item akan dikirim. Nilai defaultnya adalahBufferOverflow.SUSPEND
, yang membuat pemanggil ditangguhkan. Opsi lainnya adalahDROP_LATEST
atauDROP_OLDEST
.
MutableSharedFlow
juga memiliki properti subscriptionCount
yang berisi
jumlah kolektor aktif, sehingga Anda dapat mengoptimalkan
logika bisnis yang sesuai. MutableSharedFlow
juga berisi fungsi resetReplayCache
jika Anda tidak ingin memutar ulang informasi terbaru yang dikirim ke alur.
Referensi alur lainnya
- Alur Kotlin di Android
- Menguji flow Kotlin di Android
- Hal yang perlu diketahui tentang operator shareIn dan stateIn Alur
- Bermigrasi dari LiveData ke Alur Kotlin
- Referensi lainnya untuk coroutine dan alur Kotlin