StateFlow และ SharedFlow คือ Flow API
ที่ช่วยให้ Flow ส่งการอัปเดตสถานะและส่งค่าไปยังผู้ใช้หลายรายได้อย่างเหมาะสม
StateFlow
StateFlow
คือโฟลว์ที่สังเกตได้ซึ่งเป็นตัวยึดสถานะที่ปล่อยการอัปเดตสถานะปัจจุบันและสถานะใหม่
ไปยังผู้รวบรวม นอกจากนี้ คุณยังอ่านค่าสถานะปัจจุบันได้ผ่านพร็อพเพอร์ตี้
value
หากต้องการอัปเดตสถานะและส่งไปยังโฟลว์ ให้กำหนดค่าใหม่ให้กับพร็อพเพอร์ตี้ value ของคลาส MutableStateFlow
ใน Android StateFlow เหมาะอย่างยิ่งสำหรับคลาสที่ต้องรักษาสถานะที่เปลี่ยนแปลงได้ซึ่งสังเกตได้
จากตัวอย่างใน Kotlin Flow คุณจะแสดง StateFlow
จาก LatestNewsViewModel เพื่อให้ View สามารถ
ฟังการอัปเดตสถานะ UI และทำให้สถานะหน้าจออยู่รอดได้
เมื่อมีการเปลี่ยนแปลงการกำหนดค่า
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 UI 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 คือโปรดิวเซอร์
และคลาสทั้งหมดที่รวบรวมข้อมูลจาก StateFlow คือผู้บริโภค
flowStateFlow
การรวบรวมจากโฟลว์จะไม่ทริกเกอร์โค้ดของโปรดิวเซอร์ StateFlow
จะใช้งานอยู่เสมอและอยู่ในหน่วยความจำ และจะมีสิทธิ์สำหรับการเก็บขยะ
ก็ต่อเมื่อไม่มีการอ้างอิงอื่นๆ จากรูทการเก็บขยะ
เมื่อผู้ใช้ใหม่เริ่มรวบรวมข้อมูลจากสตรีม จะได้รับสถานะล่าสุดในสตรีมและสถานะต่อๆ ไป คุณจะเห็นลักษณะการทำงานนี้
ในคลาสที่สังเกตได้อื่นๆ เช่น
LiveData
View จะรอฟัง StateFlow เช่นเดียวกับโฟลว์อื่นๆ
class LatestNewsActivity : ComponentActivity() { private val latestNewsViewModel: LatestNewsViewModel = TODO() // 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) } } } } } }
หากต้องการแปลงโฟลว์เป็น StateFlow ให้ใช้ตัวดำเนินการระดับกลาง
stateIn
StateFlow, Flow และ LiveData
StateFlow และ LiveData มีความคล้ายคลึงกัน ทั้ง 2 คลาสเป็นคลาสที่เก็บข้อมูลที่สังเกตได้ และทั้ง 2 คลาสมีรูปแบบที่คล้ายกันเมื่อใช้ในสถาปัตยกรรมแอป
อย่างไรก็ตาม โปรดทราบว่า StateFlow และ
LiveData มีลักษณะการทำงานที่แตกต่างกัน ดังนี้
StateFlowกำหนดให้ส่งสถานะเริ่มต้นไปยังเครื่องมือสร้าง ขณะที่LiveDataไม่ได้กำหนดLiveData.observe()จะยกเลิกการลงทะเบียนผู้บริโภคโดยอัตโนมัติเมื่อ มุมมองเปลี่ยนเป็นสถานะSTOPPEDในขณะที่การรวบรวมจากStateFlowหรือ โฟลว์อื่นๆ จะไม่หยุดการรวบรวมโดยอัตโนมัติ หากต้องการให้ทำงานในลักษณะเดียวกัน คุณต้องรวบรวมโฟลว์จากบล็อกLifecycle.repeatOnLifecycle
เปลี่ยนโฟลว์ที่เย็นชาให้ร้อนแรงด้วย shareIn
StateFlow เป็นโฟลว์ฮอต ซึ่งจะยังคงอยู่ในหน่วยความจำตราบใดที่มีการรวบรวมโฟลว์หรือตราบใดที่มีการอ้างอิงอื่นๆ จากรูทของระบบจัดการหน่วยความจำที่ไม่ใช้แล้ว คุณเปลี่ยนโฟลว์แบบเย็นให้เป็นแบบร้อนได้โดยใช้ตัวดำเนินการ
shareIn
การใช้ callbackFlow ที่สร้างใน Kotlin Flows เป็นตัวอย่าง แทนที่จะให้ตัวรวบรวมแต่ละตัวสร้างโฟลว์ใหม่ คุณสามารถแชร์ข้อมูลที่ดึงมาจาก Firestore ระหว่างตัวรวบรวมได้โดยใช้ shareIn
คุณต้องส่งข้อมูลต่อไปนี้
CoroutineScopeที่ใช้แชร์โฟลว์ ขอบเขตนี้ควรมีอายุการใช้งานนานกว่าผู้บริโภครายใดๆ เพื่อให้โฟลว์ที่แชร์ยังคงใช้งานได้นานเท่าที่จำเป็น- จำนวนรายการที่จะเล่นซ้ำไปยังตัวรวบรวมข้อมูลใหม่แต่ละรายการ
- นโยบายลักษณะการทำงานเมื่อเริ่มต้น
class NewsRemoteDataSource( private val externalScope: CoroutineScope ) { val latestNews: Flow<List<ArticleHeadline>> = flow { // ... }.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed() ) }
ในตัวอย่างนี้ latestNewsโฟลว์จะเล่นซ้ำรายการที่ปล่อยออกมาล่าสุด
ไปยังตัวรวบรวมใหม่ และจะยังคงใช้งานอยู่ตราบใดที่ externalScope ยัง
ใช้งานได้และมีตัวรวบรวมที่ใช้งานอยู่ SharingStarted.WhileSubscribed()
นโยบายการเริ่มต้นจะทำให้ผู้ผลิตต้นทางยังคงใช้งานได้ในขณะที่มี
ผู้ติดตามที่ใช้งานอยู่ นอกจากนี้ ยังมีนโยบายการเริ่มต้นอื่นๆ เช่น
SharingStarted.Eagerly เริ่มการแชร์ทันทีหรือ
SharingStarted.Lazily เริ่มการแชร์หลังจากมีผู้ติดตามคนแรก
และเปิดใช้งานสตรีมต่อไปเรื่อยๆ
SharedFlow
ฟังก์ชัน shareIn จะแสดงผล SharedFlow ซึ่งเป็นโฟลว์แบบร้อนที่ปล่อยค่า
ไปยังผู้ใช้ทั้งหมดที่รวบรวมจากโฟลว์ดังกล่าว SharedFlow คือ
การสรุป StateFlow ที่กำหนดค่าได้สูง
คุณสร้าง SharedFlow ได้โดยไม่ต้องใช้ shareIn ตัวอย่างเช่น คุณ
สามารถใช้ SharedFlow เพื่อส่งเครื่องหมายถูกไปยังส่วนอื่นๆ ของแอปเพื่อให้
เนื้อหาทั้งหมดรีเฟรชเป็นระยะๆ พร้อมกัน นอกจากการดึงข้อมูลข่าวสารล่าสุดแล้ว คุณอาจต้องการรีเฟรชส่วนข้อมูลผู้ใช้ด้วยคอลเล็กชันหัวข้อโปรด ในข้อมูลโค้ดต่อไปนี้ TickHandler จะแสดง SharedFlow เพื่อให้คลาสอื่นๆ
ทราบว่าควรจะรีเฟรชเนื้อหาเมื่อใด เช่นเดียวกับ StateFlow ให้ใช้พร็อพเพอร์ตี้สนับสนุนประเภท MutableSharedFlow ในคลาสเพื่อส่งรายการไปยังโฟลว์
// 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<Unit> = _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 ได้ด้วยวิธีต่อไปนี้
replayช่วยให้คุณส่งค่าที่ส่งก่อนหน้านี้จำนวนหนึ่งอีกครั้งสำหรับผู้ติดตามใหม่onBufferOverflowช่วยให้คุณระบุนโยบายสำหรับกรณีที่บัฟเฟอร์ เต็มไปด้วยรายการที่จะส่ง ค่าเริ่มต้นคือBufferOverflow.SUSPEND, ซึ่งทำให้ผู้โทรระงับ ตัวเลือกอื่นๆ ได้แก่DROP_LATESTหรือDROP_OLDEST
MutableSharedFlow ยังมีพร็อพเพอร์ตี้ subscriptionCount ที่มี
จำนวนผู้รวบรวมข้อมูลที่ใช้งานอยู่ เพื่อให้คุณเพิ่มประสิทธิภาพตรรกะทางธุรกิจได้
ตามความเหมาะสม MutableSharedFlow ยังมีresetReplayCache
ฟังก์ชันในกรณีที่คุณไม่ต้องการเล่นข้อมูลล่าสุดที่ส่งไปยังโฟลว์ซ้ำ
แหล่งข้อมูลเพิ่มเติมเกี่ยวกับโฟลว์
- โฟลว์ Kotlin ใน Android
- การทดสอบโฟลว์ Kotlin ใน Android
- สิ่งที่ควรทราบเกี่ยวกับโอเปอเรเตอร์ shareIn และ stateIn ของ Flow
- การย้ายข้อมูลจาก LiveData ไปยัง Kotlin Flow
- โฟลว์