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 flow quan sát được chứa thông tin trạng thái. Flow này phát thông tin hiện tại và cập nhất mới cho các trình thu thập. Bạn cũng có thể đọc giá trị trạng thái hiện tại thông qua thuộc tính
value
. Để 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 các lớp cần duy trì trạng thái thay đổi được và quan sát được.
Theo ví dụ từ flow của 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 và 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(news: List<ArticleHeadline>): LatestNewsUiState()
data class Error(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 thực thể sản xuất. StateFlow
luôn hoạt động và có trong bộ nhớ, flow này chỉ đủ điều kiện để quản lý bộ nhớ
khi không có tham chiếu nào khác đến flow này
từ một gốc quản lý bộ nhớ.
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 toán tử trung gian
stateIn
.
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 chuyển một trạng thái ban đầu trong hàm dựng, cònLiveData
thì không yêu cầu như vậy.LiveData.observe()
tự động huỷ đăng ký cho thực thể tiêu tụ 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 lại. Để đạt được hành vi này, bạn cần thu thập flow từ khốiLifecycle.repeatOnLifecycle
.
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 quản lý bộ nhớ. Bạn có thể chuyển các flow bị động thành chủ động bằng cách sử dụng toán tử
shareIn
.
Lấy callbackFlow
được tạo trong flow của Kotlin làm ví dụ, thay vì để mỗi tập hợp tạo một flow mới, bạn có thể chia sẻ qua lại
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ể 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 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 giá trị
vào 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 của Kotlin trên Android
- Kiểm thử flow của 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 trong Kotlin
- Tài nguyên khác về coroutine và flow trong Kotlin