StateFlow
ve SharedFlow
, durum güncellemelerini en iyi şekilde yayınlayan ve birden fazla tüketiciye değer sunan akışları sağlayan Akış API'leridir.
StateFlow
StateFlow
, mevcut ve yeni durum güncellemelerini toplayıcılara gönderen, eyalet sahibi ve gözlemlenebilir bir akıştır. Geçerli durum değeri, value
özelliği aracılığıyla da okunabilir. Durumu güncellemek ve akışa göndermek için MutableStateFlow
sınıfının value
özelliğine yeni bir değer atayın.
Android'de StateFlow
, gözlemlenebilir değişken durumu koruması gereken sınıflar için idealdir.
Kotlin akışları ile ilgili örneklere göre, View
öğesi kullanıcı arayüzü durumu güncellemelerini dinleyebilmek ve doğal olarak ekran durumunun yapılandırma değişikliklerini atlatmasını sağlamak için LatestNewsViewModel
'den StateFlow
açığa çıkabilir.
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()
}
MutableStateFlow
güncellemesinden sorumlu olan sınıf, yapımcıdır ve StateFlow
öğesinden toplanan tüm sınıflar tüketicidir. flow
oluşturucu kullanılarak oluşturulan soğuk akıştan farklı olarak StateFlow
sıcaktır: Akıştan veri toplamak herhangi bir üretici kodunu tetiklemez. StateFlow
her zaman etkindir ve bellekte yer alır. Ayrıca, yalnızca bir çöp toplama kökünden kendisine başka referans olmadığında çöp toplama için uygun hale gelir.
Yeni bir tüketici akıştan veri toplamaya başladığında akıştaki son durumu ve sonraki durumları alır. Bu davranışı LiveData
gibi diğer gözlemlenebilir sınıflarda bulabilirsiniz.
View
, diğer akışlarda olduğu gibi StateFlow
dilini dinler:
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)
}
}
}
}
}
}
Herhangi bir akışı StateFlow
biçimine dönüştürmek için stateIn
ara operatörünü kullanın.
StateFlow, Flow ve LiveData
StateFlow
ve LiveData
arasında benzerlikler var. İkisi de gözlemlenebilir veri sahibi sınıflarıdır ve uygulama mimarinizde kullanıldıklarında benzer bir kalıp izlerler.
Ancak StateFlow
ve LiveData
değerlerinin farklı davrandığını unutmayın:
StateFlow
, oluşturucuya bir başlangıç durumunun iletilmesini gerektirirkenLiveData
bunu yapmaz.LiveData.observe()
, görüntüSTOPPED
durumuna geçtiğinde tüketicinin kaydını otomatik olarak iptal eder. Bununla birlikte,StateFlow
veya başka bir akıştan veri toplama işlemi otomatik olarak durmaz. Aynı davranışı elde etmek için birLifecycle.repeatOnLifecycle
bloğundan akışı toplamanız gerekir.
shareIn
ile soğuk akışları sıcak hale getirme
StateFlow
, sıcak bir akıştır. Akış alındığı veya bir atık toplama kökünden bu akışa başka referanslar bulunduğu sürece bellekte kalır. shareIn
operatörünü kullanarak soğuk akışları sıcak hale getirebilirsiniz.
Kotlin akışlarında oluşturulan callbackFlow
verisini örnek olarak kullanıp her toplayıcının yeni bir akış oluşturmasını istemek yerine, Firestore'dan alınan verileri shareIn
kullanarak toplayıcılar arasında paylaşabilirsiniz.
Aşağıdakileri geçmeniz gerekir:
- Akışı paylaşmak için kullanılan bir
CoroutineScope
. Ortak akışın gereken süre boyunca geçerli kalması için bu kapsamın herhangi bir tüketiciden daha uzun sürmesi gerekir. - Her yeni koleksiyoncuya tekrar oynatılacak öğe sayısı.
- Başlangıç davranış politikası.
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
Bu örnekte, latestNews
akışı, son yayınlanan öğeyi yeni bir toplayıcıya tekrar oynatır ve externalScope
yayında olduğu ve etkin toplayıcılar bulunduğu sürece etkin kalır. SharingStarted.WhileSubscribed()
başlangıç politikası, etkin aboneler olduğu sürece yukarı akış yapımcısını etkin tutar. Yapımcıyı hemen başlatmak için SharingStarted.Eagerly
veya ilk abone göründükten sonra paylaşıma başlamak ve akışı sonsuza kadar etkin tutmak için SharingStarted.Lazily
gibi başka başlatma politikaları da mevcuttur.
Paylaşılan Akış
shareIn
işlevi, bir SharedFlow
döndürür. Bu, kendisinden veri toplayan tüm tüketicilere değer yayan sıcak bir akıştır. SharedFlow
, StateFlow
öğesinin son derece yapılandırılabilir bir genellemesidir.
shareIn
kullanmadan SharedFlow
oluşturabilirsiniz. Örneğin, tüm içeriğin düzenli aralıklarla aynı anda yenilenmesi için uygulamanın geri kalanına onay işareti göndermek için SharedFlow
kullanabilirsiniz. En son haberleri getirmenin yanı sıra, kullanıcı bilgileri bölümünü favori konu koleksiyonuyla yenilemek de isteyebilirsiniz. Aşağıdaki kod snippet'inde TickHandler
, diğer sınıfların içeriğinin ne zaman yenileneceğini bilmesi için bir SharedFlow
gösteriyor. StateFlow
'de olduğu gibi, akışa öğe göndermek için sınıfta MutableSharedFlow
türünde bir yedekleme özelliği kullanın:
// 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() { ... }
...
}
SharedFlow
davranışını aşağıdaki şekillerde özelleştirebilirsiniz:
replay
, yeni aboneler için önceden yayınlanan bir dizi değeri yeniden göndermenize olanak tanır.onBufferOverflow
, arabelleğin gönderilecek öğelerle ne zaman dolduğunu belirleyen bir politika belirlemenize olanak tanır. Varsayılan değerBufferOverflow.SUSPEND
, arayanı askıya alır. Diğer seçenekler:DROP_LATEST
veyaDROP_OLDEST
.
MutableSharedFlow
, aktif toplayıcı sayısını içeren bir subscriptionCount
özelliğine de sahiptir. Böylece iş mantığınızı buna göre optimize edebilirsiniz. Akışa gönderilen en son bilgileri yeniden oynatmak istemezseniz MutableSharedFlow
bir resetReplayCache
işlevi de içerir.
Ek akış kaynakları
- Android'de Kotlin akışları
- Android'de Kotlin akışlarını test etme
- flow'un ShareIn ve stateIn operatörleri hakkında bilinmesi gerekenler
- LiveData'dan Kotlin Flow'a geçiş
- Kotlin eş yordamları ve akışı için ek kaynaklar