Android 應用架構指南

語言: CN / TW / HK

參考鏈接:應用架構指南  |  Android 開發者  |  Android Developers

Android App 用户體驗

我們常見的 Android App 一般是由四大組件組成的,其中最常見的是 Activity 和 Service 等。一個 App 可能包含多個組件,而且移動設備資源有限,系統可能會隨時終止某些 App ,鑑於這種情況,App 被銷燬是不受開發者控制的,那麼 App 的數據和狀態就不應該在內存中進行存儲,而且, 不同的組件之間不應該有依賴關係。

基於上述的一些客觀情況,Android 為我們提供了一套開發的基本規則和從系統 API 層面的規避風險的支持。但作為開發者,也要注意避免這些情況的出現。

常見的架構原則

隨着 Android 應用大小不斷增加,App 的架構應該具有支持擴展、提升穩定性和方便測試等特性。 應用架構主要是為了劃分各個部分之間的界限以及每個部分應該承擔的職責。 官方為我們指定了一些設計原則讓我們更好的去搭建和設計應用架構。

原則一:分離關注點

分離關注點(Separation of concerns,SoC),是將計算機程序分隔為不同部分的設計原則。每一部分會有自己需要關注的職責。這是最重要的原則。

一種常見的錯誤是在一個 Activity 或 Fragment 中編寫所有的代碼。這些基於界面的類應該僅處理 UI 和操作系統交互的邏輯

這些類應該儘可能的保持精簡,這樣可以避免許多與組件生命週期相關的問題,並且能夠提升這些類的可測試性。

原則二:通過數據模型驅動界面

第二個重要的原則是,應該通過數據的變化來驅動 UI 的變化。數據模型就是 Model ,代表應用的數據,它們應獨立於應用中的 UI 元素和其他組件,並且不受生命週期影響。但 Model 仍會在 App 銷燬時,從內存中移除。

持久化數據是理想之選,原因如下: * 如果 Android 操作系統銷燬應用以釋放資源,用户不會丟失數據。 * 當網絡連接不穩定或不可用時,應用可以繼續工作。

通過 Model 來驅動 UI ,另一個好處就是更方便進行測試。我們可以通過修改數據來測試 UI 的變化。

推薦的應用架構

基於上面的架構原則,App 至少應該分為兩個層級: - 界面層(UI):在屏幕上顯示應用數據。 - 數據層(Model):包含應用的業務邏輯並公開應用數據。

可以增加一箇中間的 Domain 層,來簡化和重複使用界面層與數據層之間的交互。

mad-arch-overview.png

UI 層

UI 層的作用是在屏幕上顯示應用數據。無論是因為用户互動(例如按下按鈕)還是外部輸入(例如網絡響應)導致數據發生變化時,UI 都應更新以反映相應的變化。

UI 層可以進一步拆分成兩個部分: * 在屏幕上呈現數據的 UI 元素。您可以使用 View 或 Jetpack Compose 函數構建這些元素。 * 用於存儲數據、向 UI 提供數據以及處理邏輯的狀態容器(如 ViewModel )。

mad-arch-overview-ui.png

界面的狀態

UI 層高可以拆分成 UI 元素和 UI 狀態兩部分。

mad-arch-ui-elements-state.png

UI 元素就是 UI 中具體的 View ;UI 狀態是相對於應用而言的,可以理解為 UI 的數據邏輯抽象。當數據邏輯抽象發生變化時,就會導致呈現的 UI 的變化。

一個簡單的類比是,通過 ViewModel 控制 UI 狀態。

UI 的狀態應該是定義為不可變的,這樣能夠使 UI 專注於發揮單一的作用。所以,不應該在界面中直接修改 UI 狀態,除非 UI 是狀態數據的唯一來源。

這個意思就是,相當於一個 Switch 控件,當你操作 UI 時,會使它的 一個 flag 在 true / false 之間進行切換。這就是一個 UI 是狀態數據的唯一來源。

違反這個原則會導致同一條信息有多個可信來源,從而導致數據不一致等輕微的 bug。

單向數據流管理 UI 狀態

UI 是 UI 狀態的呈現, UI 狀態是數據邏輯,在客户端中,一般數據都是動態變化的,不管是用户操作還是網絡請求,都會導致 Model 層的數據更新,進而影響到 UI 狀態的數據發生變化。

而單項數據流(一種架構模式),有助於強制實施這種健康的職責分離。

我們在上面提到過 ViewModel 組件是一個很好的狀態容器。 通過 ViewModel 組件, Model 層和 UI 層進行互動的邏輯是:

mad-arch-ui-udf.png

狀態向下流動、事件向上流動的這種模式稱為單向數據流 (UDF)。這種模式對應用架構的影響如下: * ViewModel 會存儲並公開 UI 要使用的狀態。UI 狀態是經過 ViewModel 轉換的應用數據。 * UI 元素會向 ViewModel 發送用户事件輸入的消息。 * ViewModel 會處理用户操作並更新狀態。 * 更新後的狀態將反饋給 UI 元素以進行呈現。 * 系統會對導致 UI 狀態更改的所有事件重複上述操作。

一個簡單的例子是,一個文章閲讀 App ,用户為這篇文章添加到收藏:

mad-arch-ui-udf-in-action.png

邏輯類型

上面的例子中,有一些邏輯需要進行定義或者説是為其劃分層級: - 業務邏輯:業務邏輯決定了如何處理狀態的變化,通常位於中間層或 Model 層中,但絕不能位於 UI 層中。 - 界面行為邏輯/界面邏輯:會導致 UI 呈現內容的變化,這一層邏輯應該位於 UI 層中(尤其是涉及 Context 等界面常有的類型時),而非 ViewModel 中。 如果界面變得越來越複雜,並且您希望將界面邏輯委託給另一個類,以便有利於進行測試和關注點分離,您可以創建一個簡單的類作為狀態容器。

為什麼要使用單項數據流?

mad-arch-ui-udf.png

單項數據流建立了上面這張圖的模型,種分離可讓界面只發揮其名稱所表明的作用:通過觀察 UI 狀態變化來顯示信息,並通過將這些變化傳遞給 ViewModel 來傳遞用户的輸入。 換句話説,單項數據流有助於實現以下幾點: - 數據一致性:界面只有一個可信來源。 - 可測試性:狀態來源是獨立的,因此可獨立於界面進行測試。 - 可維護性:狀態的更改遵循明確定義的模式,即狀態更改是用户事件及其數據拉取來源共同作用的結果。

而 UI 狀態這一層級,可以以 ViewModel 作為容器,以 LiveData 作為狀態的數據類傳遞工具(通過觀察者模式,可以讓 View 感知到狀態數據的變化)。

數據層

Model 層應該包含業務邏輯。業務邏輯決定應用的價值,它包含決定應用如何創建、存儲和更改數據的規則。

數據層可以由多個數據管理的 Repositories 組成,每個 Repositories 可以包含零到多個數據源。

mad-arch-overview-data.png

Repositories 類負責以下任務: * 提供數據。 * 對數據進行集中管理。 * 解決多個數據源之間的衝突。 * 包含業務邏輯。

每個 Repositories 類應僅負責處理一個數據源,該數據源可以是文件、網絡來源或本地數據庫。

需要注意的是,Model 層的數據對象會保存在內存中,當 App 被系統銷燬時,需要注意做數據的保存處理。

另一方面是數據對象應該是被強引用的,不會因為 GC 而造成數據的丟失。

Domain 層(可選層)

是位於界面與數據層之間的可選層。負責封裝複雜的業務邏輯,或者由多個 ViewModel 重複使用的簡單業務邏輯。此層是可選的,因為並非所有應用都有這類需求。請僅在需要時使用該層,例如處理複雜邏輯或支持可重用性。

總結

儘管官方的推薦的這套架構,並沒有明確指明是 MVVM ,但是從思想上還是和 MVVM 十分相似的。更準確的説,他更多的是 Presentation Model 模式的思路。 PM 的思想是將 View 抽象出一層 View 的抽象,然後通過 View 的抽象去與 Model 進行交互。 而在這個推薦的架構中,對 View 的抽象就是 UI State ,UI 元素則是指 View 。 通過 UI 狀態去與 Model 進行交互也對應了 PM 中 , Presentation Model 的角色。