做自(zì)由與創造的先行者

Android界面狀态生成

Android開(kāi)發手冊

現(xiàn)在,靜态界面已十分罕見。當用(yòng)戶與界面互動或應用(yòng)需要顯示新數據時(shí),界面狀态會(huì)發生變化。

本文(wén)檔将介紹界面狀态的生成和(hé)管理(lǐ)指南。閱讀完本文(wén)檔後,您應能(néng)夠:

了(le)解應使用(yòng)哪些(xiē) API 來(lái)生成界面狀态。這(zhè)取決于狀态容器 (state holder) 中可用(yòng)的狀态變化來(lái)源的性質,并遵循單向數據流原則。

了(le)解您應該如何限定界面狀态生成的作(zuò)用(yòng)域,以便密切注意系統資源。

了(le)解應該如何公開(kāi)界面狀态以供界面使用(yòng)。

從(cóng)根本上(shàng)說,狀态生成是将這(zhè)些(xiē)變化逐步應用(yòng)于界面狀态的過程。狀态始終存在,并且會(huì)随着事(shì)件而發生變化。

界面狀态生成流水(shuǐ)線

Android 應用(yòng)中的狀态生成可以理(lǐ)解爲一種處理(lǐ)流水(shuǐ)線,其中包含:

輸入:狀态變化的來(lái)源。這(zhè)些(xiē)來(lái)源可能(néng)包括:

界面層本地:可能(néng)是用(yòng)戶事(shì)件(例如用(yòng)戶在任務管理(lǐ)應用(yòng)中輸入“待辦事(shì)項”的标題),也(yě)可能(néng)是提供對(duì)界面邏輯的訪問權限的 API,這(zhè)些(xiē)事(shì)件/API 會(huì)導緻界面狀态發生變化。例如,在 Jetpack Compose 中對(duì) DrawerState 調用(yòng) open 方法。

界面層外(wài)部:這(zhè)些(xiē)來(lái)源來(lái)自(zì)網域層或數據層,并會(huì)導緻界面狀态發生變化。例如,從(cóng) NewsRepository 或其他(tā)事(shì)件完成加載的新聞。

以上(shàng)各項兼而有之。

狀态容器:用(yòng)于将業務邏輯和(hé)/或界面邏輯應用(yòng)于狀态變化來(lái)源并處理(lǐ)用(yòng)戶事(shì)件以生成界面狀态的類型。

輸出:應用(yòng)可以呈現(xiàn)以向用(yòng)戶提供所需信息的界面狀态。

狀态生成流水(shuǐ)線組建

後續部分将介紹各種輸入最适合采用(yòng)的狀态生成技術,以及匹配的輸出 API。每個狀态生成流水(shuǐ)線都是輸入和(hé)輸出的組合,并應滿足以下(xià)條件:

可感知(zhī)生命周期:如果界面不可見或未處于活動狀态,除非明(míng)确要求,否則狀态生成流水(shuǐ)線不應消耗任何資源。

易于使用(yòng):界面應能(néng)夠輕松呈現(xiàn)生成的界面狀态。狀态生成流水(shuǐ)線輸出的相關注意事(shì)項因不同的 View API(例如 View 系統或 Jetpack Compose)而異。

注意:在接下(xià)來(lái)的部分中,我們讨論的所有 API 都使用(yòng)慣用(yòng)的 Kotlin 和(hé) Jetpack Compose 代碼。不過,對(duì)于 Java 編程語言或 Kotlin 的其他(tā) API 中等效的類似代碼,此指南同樣适用(yòng)。

狀态生成流水(shuǐ)線中的輸入

狀态生成流水(shuǐ)線中的輸入可以通過以下(xià)方式提供其狀态變化來(lái)源:

可能(néng)是同步或異步的一次性操作(zuò),例如對(duì) suspend 函數的調用(yòng)。

流 API,例如 Flows。

以上(shàng)二者兼用(yòng)。

以下(xià)各部分介紹了(le)如何爲上(shàng)述每項輸入組建狀态生成流水(shuǐ)線。

使用(yòng)一次性 API 作(zuò)爲狀态變化來(lái)源

将 MutableStateFlow API 用(yòng)作(zuò)可觀測的可變狀态容器。在 Jetpack Compose 應用(yòng)中,您還可以考慮 mutableStateOf,尤其是在使用(yòng) Compose Text API 時(shí)。這(zhè)兩個 API 都提供了(le)允許對(duì)它們托管的值進行安全原子更新的方法(無論更新是同步的還是異步的)。

通過後台線程更改界面狀态

最好(hǎo)在主調度程序上(shàng)啓動協程以生成界面狀态。也(yě)就是說,在以下(xià)代碼段中的 withContext 代碼塊之外(wài)。不過,如果您需要在其他(tā)後台上(shàng)下(xià)文(wén)中更新界面狀态,可以通過使用(yòng)以下(xià) API 來(lái)實現(xiàn):

使用(yòng) withContext 方法可在其他(tā)并發上(shàng)下(xià)文(wén)中運行協程。

使用(yòng) MutableStateFlow 時(shí),照常使用(yòng) update 方法。

使用(yòng) Compose State 時(shí),使用(yòng) Snapshot.withMutableSnapshot 來(lái)保證在并發上(shàng)下(xià)文(wén)中對(duì) State 進行原子更新。

使用(yòng)流 API 作(zuò)爲狀态變化來(lái)源

對(duì)于随着時(shí)間推移連續生成多個值的狀态變化來(lái)源,一種簡單直接的狀态生成方法是将所有來(lái)源的輸出聚合爲一個緊密的整體。

使用(yòng) Kotlin Flow 時(shí),您可以通過 combine 函數來(lái)實現(xiàn)此目的。您可在 InterestsViewModel 中的“Now in Android”示例中查看(kàn)示例:

class InterestsViewModel(

authorsRepository: AuthorsRepository,

topicsRepository: TopicsRepository

) : ViewModel() {

val uiState = combine(

authorsRepository.getAuthorsStream(),

topicsRepository.getTopicsStream(),

) { availableAuthors, availableTopics ->

InterestsUiState.Interests(

authors = availableAuthors,

topics = availableTopics

)

}

.stateIn(

scope = viewModelScope,

started = SharingStarted.WhileSubscribed(5_000),

initialValue = InterestsUiState.Loading

)

}

注意:您可以使用(yòng) stateIn 運算(suàn)符将組合 Flow 轉換爲 StateFlow 作(zuò)爲界面狀态的可觀測 API。

使用(yòng) stateIn 運算(suàn)符創建 StateFlows,可使界面能(néng)夠更精細地控制狀态生成流水(shuǐ)線的活動,因爲它可能(néng)隻需在界面可見時(shí)才處于活動狀态。

如果僅當界面可見,同時(shí)正在以生命周期感知(zhī)方式收集數據流時(shí),流水(shuǐ)線才應處于活動狀态,則使用(yòng) SharingStarted.WhileSubscribed()。

如果隻要用(yòng)戶可能(néng)返回界面(即界面位于後台堆棧上(shàng)或屏幕外(wài)的其他(tā)标簽頁中),流水(shuǐ)線就應該處于活動狀态,則使用(yòng) SharingStarted.Lazily。

如果聚合基于數據流的狀态來(lái)源不适用(yòng),Kotlin Flow 等流 API 可提供一組豐富的轉換(例如合并、扁平化等),以幫助将數據流處理(lǐ)爲界面狀态。

要點:在大(dà)多數情況下(xià),combine 都是從(cóng)流 API 生成狀态的可行方法。

使用(yòng)一次性的流 API 作(zuò)爲狀态變化來(lái)源

如果狀态生成流水(shuǐ)線依賴一次性調用(yòng)和(hé)數據流作(zuò)爲狀态變化來(lái)源,則數據流就是決定性的約束條件。因此,應将一次性調用(yòng)轉換爲數據流 API,或将其輸出傳輸到(dào)數據流中并恢複處理(lǐ),如上(shàng)文(wén)的數據流部分中所述。

對(duì)于數據流,這(zhè)通常意味着創建一個或多個專用(yòng)後備 MutableStateFlow 實例以傳播狀态變化。您還可以從(cóng) Compose 狀态創建快(kuài)照流。

狀态生成流水(shuǐ)線初始化

初始化狀态生成流水(shuǐ)線需要設置流水(shuǐ)線運行的初始條件。這(zhè)可能(néng)涉及提供對(duì)啓動流水(shuǐ)線至關重要的初始輸入值(例如,适用(yòng)于新聞報(bào)道(dào)詳情視(shì)圖的 id),或提供對(duì)啓動異步加載至關重要的初始輸入值。

您應該盡可能(néng)延遲狀态生成流水(shuǐ)線的初始化,以節省系統資源。實際上(shàng),這(zhè)通常意味着等到(dào)出現(xiàn)輸出的使用(yòng)方。Flow API 通過 stateIn 方法中的 started 參數允許執行此操作(zuò)。如果這(zhè)種做法不适用(yòng),請(qǐng)定義幂等 initialize() 函數,以明(míng)确啓動狀态生成流水(shuǐ)線,如以下(xià)代碼段所示:

class MyViewModel : ViewModel() {

private var initializeCalled = false

// This function is idempotent provided it is only called from the UI thread.

@MainThread

fun initialize() {

if(initializeCalled) return

initializeCalled = true

viewModelScope.launch {

// seed the state production pipeline

}

}

}

警告:請(qǐng)避免在 ViewModel 的 init 塊或構造函數中啓動異步操作(zuò)。異步操作(zuò)不應是創建對(duì)象時(shí)的附帶效應,因爲異步代碼在對(duì)象完全初始化之前可能(néng)會(huì)對(duì)該對(duì)象執行讀寫操作(zuò)。這(zhè)也(yě)稱爲對(duì)象洩露,可能(néng)會(huì)導緻細微且難以診斷的錯誤。使用(yòng) Compose 狀态時(shí),這(zhè)一點尤爲重要。當 ViewModel 存儲了(le) Compose 狀态字段時(shí),請(qǐng)勿在更新 Compose 狀态字段的 ViewModel 的 init 塊中啓動協程,否則可能(néng)會(huì)出現(xiàn) IllegalStateException。

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:Android網域層
上(shàng)一篇:Android狀态容器和(hé)界面狀态