StateFlow
và SharedFlow
là các API về flow (dòng dữ liệu) cho phép flow phát thông tin cập nhật trạng thái và phát giá trị cho nhiều thực thể tiêu thụ một cách tối ưu.
StateFlow
StateFlow
là một luồng có thể quan sát của phần tử giữ trạng thái phát ra trạng thái hiện tại và trạng thái mới
cho trình thu thập. Giá trị trạng thái hiện tại cũng có thể được đọc qua
value
thuộc tính này. Để cập nhật trạng thái và gửi trạng thái này cho flow, hãy gán một giá trị mới cho
thuộc tính value
của lớp
MutableStateFlow
.
Trong Android, StateFlow
là lựa chọn phù hợp cho những lớp cần duy trì trạng thái thay đổi được và quan sát được.
Theo ví dụ từ flow Kotlin, một StateFlow
có thể được cung cấp từ LatestNewsViewModel
để View
có thể theo dõi thông tin cập nhật trạng thái giao diện người dùng, đồng thời giúp trạng thái màn hình duy trì sau khi thay đổi cấu hình.
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()
}
Lớp chịu trách nhiệm cập nhật MutableStateFlow
là thực thể sản xuất và
tất cả các lớp thu thập từ StateFlow
đều là thực thể tiêu thụ. Không giống như
flow bị động (cold) được tạo bằng hàm tạo flow
, StateFlow
có trạng thái chủ động (hot):
việc thu thập từ flow này không kích hoạt mã thực thể sản xuất. StateFlow
luôn hoạt động và có trong bộ nhớ, luồng này chỉ đủ điều kiện để thu gom rác khi không có tham chiếu nào khác đến luồng này từ một gốc thu gom rác.
Khi thực thể tiêu thụ mới bắt đầu thu thập từ flow, thực thể này sẽ nhận được trạng thái
mới nhất trong luồng dữ liệu và mọi trạng thái sau đó. Bạn có thể tìm thấy hành vi này trong các lớp khác quan sát được như
LiveData
.
View
theo dõi StateFlow
như với bất kỳ flow nào khác:
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)
}
}
}
}
}
}
Để chuyển đổi bất kỳ flow nào sang StateFlow
, hãy sử dụng hàm
stateIn
toán tử trung gian.
StateFlow, Flow và LiveData
StateFlow
và LiveData
có nhiều điểm tương đồng. Cả hai đều là lớp quan sát được chứa dữ liệu và cả hai đều hoạt động theo cùng một mẫu tương tự khi được sử dụng trong kiến trúc ứng dụng của bạn.
Tuy nhiên, hãy lưu ý rằng StateFlow
và
LiveData
hoạt động theo cách khác nhau:
StateFlow
yêu cầu truyền một trạng thái ban đầu trong hàm khởi tạo trong khiLiveData
thì không yêu cầu.LiveData.observe()
tự động huỷ đăng ký cho thực thể tiêu thụ khi khung hình chuyển sang trạng tháiSTOPPED
, trong khi quá trình thu thập từStateFlow
hoặc bất kỳ flow nào khác sẽ không tự động ngừng thu thập. Để đạt được cùng một mục tiêu nên bạn cần thu thập flow từLifecycle.repeatOnLifecycle
chặn.
Chuyển flow bị động thành chủ động bằng cách sử dụng shareIn
StateFlow
là flow chủ động – flow này luôn nằm trong bộ nhớ trong khoảng thời gian được thu thập
hoặc trong khi mọi tham chiếu khác đến flow này tồn tại từ một gốc thu gom rác. Bạn có thể chuyển các flow bị động thành chủ động bằng cách sử dụng
shareIn
toán tử.
Lấy callbackFlow
được tạo trong flow Kotlin làm ví dụ, thay vì để mỗi trình thu thập tạo một flow mới, bạn có thể chia sẻ dữ liệu truy xuất từ Firestore giữa các trình thu thập bằng cách sử dụng shareIn
.
Bạn cần chuyển những thông tin sau:
CoroutineScope
dùng để chia sẻ flow. Phạm vi này sẽ tồn tại lâu hơn bất kỳ thực thể tiêu thụ nào để duy trì flow được chia sẻ trong khoảng thời gian cần thiết.- Số mục phát lại cho mỗi trình thu thập mới.
- Chính sách về hành vi bắt đầu.
class NewsRemoteDataSource(...,
private val externalScope: CoroutineScope,
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
...
}.shareIn(
externalScope,
replay = 1,
started = SharingStarted.WhileSubscribed()
)
}
Trong ví dụ này, flow latestNews
sẽ phát lại mục được phát gần đây nhất
cho trình thu thập mới và vẫn hoạt động miễn là externalScope
còn hoạt động và còn có các trình thu thập đang hoạt động. Chính sách bắt đầu của SharingStarted.WhileSubscribed()
giúp thực thể sản xuất của luồng ngược dòng (upstream) hoạt động trong khi có các trình theo dõi
đang hoạt động. Chúng tôi cung cấp các chính sách bắt đầu khác, chẳng hạn như
SharingStarted.Eagerly
để bắt đầu thực thể sản xuất ngay lập tức hoặc
SharingStarted.Lazily
để bắt đầu chia sẻ sau khi trình theo dõi đầu tiên xuất hiện
và giữ cho flow hoạt động vĩnh viễn.
SharedFlow
Hàm shareIn
trả về một SharedFlow
, một flow chủ động phát ra giá trị
cho tất cả thực thể tiêu thụ thu thập từ flow đó. SharedFlow
là mô hình tổng quát của StateFlow
với khả năng định cấu hình linh hoạt.
Bạn có thể tạo SharedFlow
mà không cần sử dụng shareIn
. Ví dụ: bạn có thể sử dụng SharedFlow
để gửi kim đánh dấu nhịp độ khung hình đến những phần khác của ứng dụng để tất cả nội dung được làm mới định kỳ cùng một lúc. Ngoài việc tìm nạp tin tức mới nhất, bạn cũng có thể làm mới mục thông tin người dùng bằng tập hợp các chủ đề mà họ yêu thích. Trong đoạn mã sau đây, TickHandler
sẽ cung cấp một SharedFlow
để các lớp khác biết thời điểm làm mới nội dung của mình. Tương tự như với StateFlow
, hãy sử dụng thuộc tính dự phòng thuộc loại MutableSharedFlow
trong một lớp để gửi các mục đến flow:
// 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() { ... }
...
}
Bạn có thể tuỳ chỉnh hành vi của SharedFlow
theo các cách sau:
replay
cho phép bạn gửi lại một số giá trị đã phát trước đó cho các trình theo dõi mới.onBufferOverflow
cho phép bạn chỉ định chính sách cho thời điểm vùng đệm đã đầy các mục cần gửi. Giá trị mặc định làBufferOverflow.SUSPEND
, khiến phương thức gọi bị tạm ngưng. Các tuỳ chọn khác làDROP_LATEST
hoặcDROP_OLDEST
.
MutableSharedFlow
cũng có một thuộc tính subscriptionCount
chứa số lượng trình thu thập đang hoạt động để bạn có thể tối ưu hoá logic nghiệp vụ của mình cho phù hợp. MutableSharedFlow
cũng chứa một hàm resetReplayCache
nếu bạn không muốn phát lại thông tin mới nhất được gửi đến flow này.
Các tài nguyên khác về flow
- Flow Kotlin trên Android
- Thử nghiệm flow Kotlin trên Android
- Những điều cần biết về toán tử sharedIn và stateIn của Flow
- Di chuyển từ LiveData sang Flow Kotlin
- Tài nguyên khác về coroutine và flow Kotlin