StateFlow และ SharedFlow

StateFlow และ SharedFlow เป็น Flow API ที่ช่วยให้โฟลว์แสดงการอัปเดตสถานะและแสดงค่าไปยังหลายค่าได้อย่างมีประสิทธิภาพ ผู้บริโภค

StateFlow

StateFlow เป็นโฟลว์ที่สังเกตได้ของผู้ถือสถานะซึ่งจะแสดงสถานะปัจจุบันและสถานะใหม่ ใหม่ๆ สำหรับผู้รวบรวม ค่าสถานะปัจจุบันยังอ่านได้ผ่านทาง value หากต้องการอัปเดตสถานะและส่งไปยังโฟลว์ ให้กำหนดค่าใหม่ให้กับ พร็อพเพอร์ตี้ value ของ MutableStateFlow ชั้นเรียน

ใน Android StateFlow เหมาะอย่างยิ่งสำหรับชั้นเรียนที่ต้องบำรุงรักษา สถานะที่เปลี่ยนแปลงได้

จากตัวอย่างจากขั้นตอนของ Kotlin ซึ่งเป็น 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 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 จะเป็นผู้สร้าง และคลาสทั้งหมดที่รวบรวมจาก StateFlow คือผู้บริโภค เลิกชอบ ขั้นตอน Cold ที่สร้างขึ้นโดยใช้เครื่องมือสร้าง flow ซึ่ง StateFlow มีความร้อนดังนี้ ที่รวบรวมจากขั้นตอนจะไม่เรียกใช้โค้ดของผู้ผลิต StateFlow ทำงานและอยู่ในหน่วยความจำเสมอ และมีสิทธิ์ใช้พื้นที่เก็บข้อมูลขยะ ของคุณก็ต่อเมื่อไม่มีการอ้างอิงถึงไฟล์อื่นจากถังขยะ คอลเล็กชัน

เมื่อผู้บริโภคใหม่เริ่มเก็บข้อมูลจากขั้นตอน ผู้บริโภคจะได้รับ ในสตรีมและสถานะต่อๆ ไป คุณจะเห็นลักษณะการทำงานนี้ ในชั้นเรียนอื่นๆ ที่สังเกตได้ เช่น LiveData

View จะฟัง StateFlow เช่นเดียวกับขั้นตอนอื่นๆ:

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)
                    }
                }
            }
        }
    }
}

หากต้องการแปลงขั้นตอนเป็น StateFlow ให้ใช้ stateIn ตัวกลาง

StateFlow, Flow และ LiveData

StateFlow และ LiveData มี ความคล้ายคลึงกัน ทั้ง 2 คลาสเป็นคลาสผู้ถือข้อมูลที่สังเกตได้ และทั้ง 2 แบบตามหลัง รูปแบบที่คล้ายกันเมื่อใช้ในสถาปัตยกรรมแอปของคุณ

อย่างไรก็ตาม โปรดทราบว่า StateFlow และ LiveData มีลักษณะการทำงานที่ต่างออกไป ดังนี้

  • StateFlow กำหนดให้ส่งผ่านสถานะเริ่มต้นไปยังเครื่องมือสร้าง แต่ LiveData ไม่มี
  • LiveData.observe() จะยกเลิกการลงทะเบียนผู้ใช้ทั่วไปโดยอัตโนมัติเมื่อ มุมมองจะเป็นสถานะ STOPPED ในขณะที่รวบรวมจาก StateFlow หรือ ขั้นตอนอื่นๆ จะไม่หยุดการรวบรวมโดยอัตโนมัติ เพื่อให้ได้เป้าหมายเดียวกัน คุณต้องรวบรวมโฟลว์จาก Lifecycle.repeatOnLifecycle บล็อก

ทำให้กระแสเย็นเป็นอุณหภูมิที่ร้อนโดยใช้ shareIn

StateFlow เป็นโฟลว์ฮอต ซึ่งจะอยู่ในหน่วยความจำตราบเท่าที่ดำเนินการอยู่ ที่เก็บรวบรวมไว้ หรือในขณะที่มีการอ้างอิงอื่นๆ ถึงข้อมูลนั้นมาจากถังขยะ ราก คุณสามารถเปลี่ยนการไหลของความเย็นได้โดยใช้ shareIn

การใช้ callbackFlow ที่สร้างขึ้นในขั้นตอน Kotlin เป็น เช่น แทนที่จะให้ผู้รวบรวมแต่ละรายสร้างขั้นตอนใหม่ คุณก็แชร์ ข้อมูลที่เรียกจาก 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<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 ได้ด้วยวิธีต่อไปนี้

  • replay ให้คุณส่งจำนวนค่าที่เคยปล่อยไว้ก่อนหน้าสำหรับฟังก์ชันใหม่ คน
  • onBufferOverflow ให้คุณระบุนโยบายเมื่อบัฟเฟอร์ มีรายการที่จะส่งทั้งหมด ค่าเริ่มต้นคือ BufferOverflow.SUSPEND ซึ่งจะทำให้ผู้โทรระงับการใช้งาน ตัวเลือกอื่นๆ ได้แก่ DROP_LATEST หรือ DROP_OLDEST

MutableSharedFlow ยังมีพร็อพเพอร์ตี้ subscriptionCount ที่มี จำนวนผู้สะสมที่ใช้งานอยู่ เพื่อให้คุณเพิ่มประสิทธิภาพให้ธุรกิจได้ ตามความเหมาะสม MutableSharedFlow มี resetReplayCache ด้วย หากคุณไม่ต้องการเล่นซ้ำข้อมูลล่าสุดที่ส่งไปยังโฟลว์

แหล่งข้อมูลขั้นตอนเพิ่มเติม