StateFlow
ve SharedFlow
, akışların durum güncellemelerini ve değerleri birden fazla tüketiciye optimum şekilde yayınlamasını sağlayan akış API'leridir.
StateFlow
StateFlow
, geçerli ve yeni durum güncellemelerini toplayıcılarına yayan, durum bağlı olduğu, 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
mülküne yeni bir değer atayın.
Android'de StateFlow
, gözlemlenebilir ve değişken bir durumu koruması gereken sınıflar için mükemmel bir seçimdir.
Kotlin akışlarındaki örnekleri takip ederek View
'in kullanıcı arayüzü durum güncellemelerini dinleyebilmesi ve ekran durumunun yapılandırma değişikliklerinden etkilenmemesi için LatestNewsViewModel
'ten bir StateFlow
gösterilebilir.
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()
}
Bir MutableStateFlow
öğesini güncellemekten sorumlu sınıf üreticidir ve StateFlow
öğesinden veri toplayan tüm sınıflar tüketicidir. flow
oluşturucu kullanılarak oluşturulan soğuk akışın aksine StateFlow
sıcaktır: Akıştan veri toplamak herhangi bir üretici kodunu tetiklemez. StateFlow
her zaman etkindir ve bellekte bulunur. Yalnızca bir çöp toplama kökünden 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 gözlemlenebilir diğer sınıflarda da bulabilirsiniz.
View
, diğer tüm akışlarda olduğu gibi StateFlow
için 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
olarak dönüştürmek için stateIn
ara operatörünü kullanın.
StateFlow, Flow ve LiveData
StateFlow
ve LiveData
benzerliklere sahiptir. Her ikisi de gözlemlenebilir veri tutucu sınıflarıdır ve uygulama mimarinizde kullanıldığında benzer bir kalıp izler.
Ancak StateFlow
ve LiveData
'nin farklı davrandığını unutmayın:
StateFlow
, oluşturucuya bir başlangıç durumunun iletilmesini gerektirir.LiveData
ise bunu gerektirmez.LiveData.observe()
, görüntülemeSTOPPED
durumuna geçtiğinde tüketicinin kaydını otomatik olarak siler.StateFlow
veya başka bir akıştan veri toplama işlemi ise otomatik olarak durdurulmaz. Aynı davranışı elde etmek için akışı birLifecycle.repeatOnLifecycle
bloğundan toplamanız gerekir.
shareIn
kullanarak soğuk akışları sıcak hale getirme
StateFlow
, sıcak bir akıştır. Akış toplandığı veya bir çöp toplama kökünden ona başka referanslar olduğu sürece bellekte kalır. shareIn
operatörünü kullanarak soğuk akışları sıcak duruma getirebilirsiniz.
Kotlin akışlarında oluşturulan callbackFlow
örneğini kullanarak, her toplayıcının yeni bir akış oluşturması yerine shareIn
kullanarak Firestore'dan alınan verileri toplayıcılar arasında paylaşabilirsiniz.
Aşağıdakileri iletmeniz gerekir:
- Akış paylaşmak için kullanılan bir
CoroutineScope
. Bu kapsam, paylaşılan akışı gerektiği kadar uzun süre etkin tutmak için tüm tüketicilerden daha uzun süre etkin kalmalıdır. - Her yeni toplayıcıya yeniden 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ışı, yayınlanan son öğeyi yeni bir toplayıcıya tekrar oynatır ve externalScope
canlı olduğu ve etkin toplayıcılar olduğu sürece etkin kalır. SharingStarted.WhileSubscribed()
Başlatma politikası, etkin aboneler olduğu sürece yayın öncesi üreticiyi etkin tutar. SharingStarted.Eagerly
Üretici akışını hemen başlatma veya SharingStarted.Lazily
ilk abone göründükten sonra paylaşımı başlatıp akışı sonsuza kadar etkin tutma gibi başka başlangıç politikaları da vardır.
SharedFlow
shareIn
işlevi, veri toplayan tüm tüketicilere değerler yayan bir sıcak akış olan SharedFlow
döndürür. SharedFlow
, StateFlow
'un yüksek oranda yapılandırılabilir bir genellemesidir.
shareIn
kullanmadan SharedFlow
oluşturabilirsiniz. Örneğin, tüm içeriğin düzenli olarak aynı anda yenilenmesi için uygulamanın geri kalanına onay işaretleri göndermek üzere bir SharedFlow
kullanabilirsiniz. En son haberleri getirmenin yanı sıra kullanıcı bilgileri bölümünü favori konular koleksiyonuyla yenilemek de isteyebilirsiniz. Aşağıdaki kod snippet'inde, diğer sınıfların içeriğini ne zaman yenileyeceğini bilmesi için bir TickHandler
, bir SharedFlow
gösterir. StateFlow
ile olduğu gibi, öğeleri akışa göndermek için sınıfta MutableSharedFlow
türüne sahip bir destekleyici mülk 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
, daha önce yayınlanan bir dizi değeri yeni aboneler için yeniden göndermenize olanak tanır.onBufferOverflow
, arabelleğin gönderilecek öğelerle dolduğu durumlar için bir politika belirtmenize olanak tanır. Varsayılan değerBufferOverflow.SUSPEND
'tır ve arayanın askıya alınmasına neden olur. Diğer seçeneklerDROP_LATEST
veyaDROP_OLDEST
'dur.
MutableSharedFlow
, etkin toplayıcı sayısını içeren bir subscriptionCount
özelliğine de sahiptir. Böylece iş mantığınızı buna göre optimize edebilirsiniz. MutableSharedFlow
, akışa gönderilen en son bilgileri yeniden oynatmak istemiyorsanız 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 bilmeniz gerekenler
- LiveData'dan Kotlin Flow'a geçiş
- Kotlin iş parçacıkları ve akış için ek kaynaklar