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

Android應用(yòng)架構指南

Android開(kāi)發手冊

移動應用(yòng)用(yòng)戶體驗

典型的 Android 應用(yòng)包含多個應用(yòng)組件,包括 Activity、Fragment、Service、内容提供程序和(hé)廣播接收器。您需要在應用(yòng)清單中聲明(míng)其中的大(dà)多數應用(yòng)組件。Android 操作(zuò)系統随後會(huì)使用(yòng)此文(wén)件來(lái)決定如何将您的應用(yòng)集成到(dào)設備的整體用(yòng)戶體驗中。鑒于典型的 Android 應用(yòng)可能(néng)包含多個組件,并且用(yòng)戶經常會(huì)在短時(shí)間内與多個應用(yòng)進行互動,因此應用(yòng)需要适應不同類型的用(yòng)戶驅動型工(gōng)作(zuò)流和(hé)任務。

請(qǐng)注意,移動設備的資源也(yě)很(hěn)有限,因此操作(zuò)系統可能(néng)随時(shí)終止某些(xiē)應用(yòng)進程以便爲新的進程騰出空(kōng)間。

鑒于這(zhè)種環境條件,您的應用(yòng)組件可以不按順序地單獨啓動,并且操作(zuò)系統或用(yòng)戶可以随時(shí)銷毀它們。由于這(zhè)些(xiē)事(shì)件不受您的控制,因此您不應在内存中存儲或保留任何應用(yòng)數據或狀态,并且應用(yòng)組件不應相互依賴。

常見的架構原則

如果您不應使用(yòng)應用(yòng)組件存儲應用(yòng)數據和(hé)狀态,那麽您應該改爲如何設計(jì)應用(yòng)呢(ne)?

随着 Android 應用(yòng)大(dà)小(xiǎo)不斷增加,您定義的架構務必要能(néng)允許應用(yòng)擴縮、提升應用(yòng)的穩健性并且方便對(duì)應用(yòng)進行測試。

應用(yòng)架構定義了(le)應用(yòng)的各個部分之間的界限以及每個部分應承擔的職責。爲了(le)滿足上(shàng)述需求,您應該按照某些(xiē)特定原則設計(jì)應用(yòng)架構。

分離關注點

要遵循的最重要的原則是分離關注點。 一種常見的錯誤是在一個 Activity 或 Fragment 中編寫所有代碼。這(zhè)些(xiē)基于界面的類應僅包含處理(lǐ)界面和(hé)操作(zuò)系統交互的邏輯。您應使這(zhè)些(xiē)類盡可能(néng)保持精簡,這(zhè)樣可以避免許多與組件生命周期相關的問題,并提高(gāo)這(zhè)些(xiē)類的可測試性。

請(qǐng)注意,您并非擁有 Activity 和(hé) Fragment 的實現(xiàn);它們隻是表示 Android 操作(zuò)系統與應用(yòng)之間關系的粘合類。操作(zuò)系統可能(néng)會(huì)根據用(yòng)戶互動或因内存不足等系統條件随時(shí)銷毀它們。爲了(le)提供令人滿意的用(yòng)戶體驗和(hé)更易于管理(lǐ)的應用(yòng)維護體驗,最好(hǎo)盡量減少對(duì)它們的依賴。

通過數據模型驅動界面

另一個重要原則是您應該通過數據模型驅動界面(最好(hǎo)是持久性模型)。數據模型代表應用(yòng)的數據。它們獨立于應用(yòng)中的界面元素和(hé)其他(tā)組件。這(zhè)意味着它們與界面和(hé)應用(yòng)組件的生命周期沒有關聯,但(dàn)仍會(huì)在操作(zuò)系統決定從(cóng)内存中移除應用(yòng)的進程時(shí)被銷毀。

持久性模型是理(lǐ)想之選,原因如下(xià):

如果 Android 操作(zuò)系統銷毀應用(yòng)以釋放(fàng)資源,用(yòng)戶不會(huì)丢失數據。

當網絡連接不穩定或不可用(yòng)時(shí),應用(yòng)會(huì)繼續工(gōng)作(zuò)。

如果您的應用(yòng)架構以數據模型類爲基礎,您的應用(yòng)會(huì)更便于測試、更穩定可靠。

單一數據源

在應用(yòng)中定義新數據類型時(shí),您應爲其分配單一數據源 (SSOT)。SSOT 是該數據的所有者,而且隻有此 SSOT 可以修改或轉變該數據。爲了(le)實現(xiàn)這(zhè)一點,SSOT 會(huì)以不可變類型公開(kāi)數據;而且爲了(le)修改數據,SSOT 會(huì)公開(kāi)函數或接收其他(tā)類型可以調用(yòng)的事(shì)件。

此模式具有多種優勢:

将對(duì)特定類型數據的所有更改集中到(dào)一處。

保護數據,防止其他(tā)類型篡改此數據。

更易于跟蹤對(duì)數據的更改。因此,更容易發現(xiàn) bug。

在離線優先應用(yòng)中,應用(yòng)數據的單一數據源通常是數據庫。在其他(tā)某些(xiē)情況下(xià),單一數據源可以是 ViewModel 甚至是界面。

單向數據流

在我們的指南中,單一數據源原則常常與單向數據流 (UDF) 模式一起使用(yòng)。在 UDF 中,狀态僅朝一個方向流動。修改數據的事(shì)件朝相反方向流動。

在 Android 中,狀态或數據通常從(cóng)分區(qū)層次結構中較高(gāo)的分區(qū)類型流向較低(dī)的分區(qū)類型。事(shì)件通常在分區(qū)層次結構中較低(dī)的分區(qū)類型觸發,直到(dào)其到(dào)達 SSOT 的相應數據類型。例如,應用(yòng)數據通常從(cóng)數據源流向界面。用(yòng)戶事(shì)件(例如按鈕按下(xià)操作(zuò))從(cóng)界面流向 SSOT,在 SSOT 中應用(yòng)數據被修改并以不可變類型公開(kāi)。

此模式可以更好(hǎo)地保證數據一緻性,不易出錯、更易于調試,并且具備 SSOT 模式的所有優勢。

推薦的應用(yòng)架構

本部分将演示如何按照建議(yì)的最佳做法構建應用(yòng)。

注意:本頁中提供的建議(yì)和(hé)最佳實踐可應用(yòng)于各種應用(yòng)。遵循這(zhè)些(xiē)建議(yì)和(hé)最佳實踐可以提升應用(yòng)的可擴展性、質量和(hé)穩健性,并可使應用(yòng)更易于測試。不過,您應該将這(zhè)些(xiē)提示視(shì)爲指南,并視(shì)需要進行調整來(lái)滿足您的要求。

基于上(shàng)一部分提到(dào)的常見架構原則,每個應用(yòng)應至少有兩個層:

界面層 - 在屏幕上(shàng)顯示應用(yòng)數據。

數據層 - 包含應用(yòng)的業務邏輯并公開(kāi)應用(yòng)數據。

您可以額外(wài)添加一個名爲“網域層”的架構層,以簡化和(hé)重複使用(yòng)界面層與數據層之間的交互。

現(xiàn)代應用(yòng)架構

此現(xiàn)代應用(yòng)架構鼓勵采用(yòng)以下(xià)方法及其他(tā)一些(xiē)方法:

反應式分層架構。

應用(yòng)的所有層中的單向數據流 (UDF)。

包含狀态容器的界面層,用(yòng)于管理(lǐ)界面的複雜(zá)性。

協程和(hé)數據流。

依賴項注入最佳實踐。

如需了(le)解詳情,請(qǐng)參閱以下(xià)部分、目錄中的其他(tā)“架構”頁面以及包含最重要的最佳實踐摘要的“建議(yì)”頁面。

界面層

界面層(或呈現(xiàn)層)的作(zuò)用(yòng)是在屏幕上(shàng)顯示應用(yòng)數據。每當數據發生變化時(shí),無論是因爲用(yòng)戶互動(例如按了(le)某個按鈕),還是因爲外(wài)部輸入(例如網絡響應),界面都應随之更新,以反映這(zhè)些(xiē)變化。

界面層由以下(xià)兩部分組成:

在屏幕上(shàng)呈現(xiàn)數據的界面元素。您可以使用(yòng) View 或 Jetpack Compose 函數構建這(zhè)些(xiē)元素。

用(yòng)于存儲數據、向界面提供數據以及處理(lǐ)邏輯的狀态容器(如 ViewModel 類)。

數據層

應用(yòng)的數據層包含業務邏輯。業務邏輯決定應用(yòng)的價值,它包含決定應用(yòng)如何創建、存儲和(hé)更改數據的規則。

數據層由多個存儲庫組成,其中每個存儲庫都可以包含零到(dào)多個數據源。您應該爲應用(yòng)中處理(lǐ)的每種不同類型的數據分别創建一個存儲庫類。例如,您可以爲與電影相關的數據創建一個 MoviesRepository 類,或者爲與付款相關的數據創建一個 PaymentsRepository 類。

圖 3. 數據層在應用(yòng)架構中的作(zuò)用(yòng)。

存儲庫類負責以下(xià)任務:

向應用(yòng)的其餘部分公開(kāi)數據。

集中處理(lǐ)數據變化。

解決多個數據源之間的沖突。

對(duì)應用(yòng)其餘部分的數據源進行抽象化處理(lǐ)。

包含業務邏輯。

每個數據源類應僅負責處理(lǐ)一個數據源,數據源可以是文(wén)件、網絡來(lái)源或本地數據庫。數據源類是應用(yòng)與數據操作(zuò)系統之間的橋梁。

如需詳細了(le)解此層,請(qǐng)參閱數據層頁面。

網域層

網域層是位于界面與數據層之間的可選層。

網域層負責封裝複雜(zá)的業務邏輯,或者由多個 ViewModel 重複使用(yòng)的簡單業務邏輯。此層是可選的,因爲并非所有應用(yòng)都有這(zhè)類需求。請(qǐng)僅在需要時(shí)使用(yòng)該層,例如處理(lǐ)複雜(zá)邏輯或支持可重用(yòng)性。

此層中的類通常稱爲“用(yòng)例”或“交互方”。每個用(yòng)例都應僅負責單個功能(néng)。例如,如果多個 ViewModel 依賴時(shí)區(qū)在屏幕上(shàng)顯示适當的消息,則您的應用(yòng)可能(néng)具有 GetTimeZoneUseCase 類。

如需詳細了(le)解此層,請(qǐng)參閱網域層頁面。

管理(lǐ)組件之間的依賴關系

應用(yòng)中的類要依賴其他(tā)類才能(néng)正常工(gōng)作(zuò)。您可以使用(yòng)以下(xià)任一設計(jì)模式來(lái)收集特定類的依賴項:

依賴注入 (DI):依賴注入使類能(néng)夠定義其依賴項而不構造它們。在運行時(shí),另一個類負責提供這(zhè)些(xiē)依賴項。

服務定位器:服務定位器模式提供了(le)一個注冊表,類可以從(cóng)中獲取其依賴項而不構造它們。

您可以借助這(zhè)些(xiē)模式來(lái)擴展代碼,因爲它們可提供清晰的依賴項管理(lǐ)模式(無需複制代碼,也(yě)不會(huì)增添複雜(zá)性)。 此外(wài),您還可以借助這(zhè)些(xiē)模式在測試和(hé)生産實現(xiàn)之間快(kuài)速切換。

我們建議(yì)在 Android 應用(yòng)中采用(yòng)依賴項注入模式并使用(yòng) Hilt 庫。Hilt 通過遍曆依賴項樹自(zì)動構造對(duì)象,爲依賴項提供編譯時(shí)保證,并爲 Android 框架類創建依賴項容器。

常見的最佳實踐

編程是一個創造性的領域,構建 Android 應用(yòng)也(yě)不例外(wài)。 無論是在多個 activity 或 fragment 之間傳遞數據,檢索遠程數據并将其保留在本地以在離線模式下(xià)使用(yòng),還是複雜(zá)應用(yòng)遇到(dào)的任何其他(tā)常見情況,解決問題的方法都會(huì)有很(hěn)多種。

雖然以下(xià)建議(yì)不是強制性的,但(dàn)在大(dà)多數情況下(xià),遵循這(zhè)些(xiē)建議(yì)會(huì)使您的代碼庫更強大(dà)、可測試性更高(gāo)且更易維護:

不要将數據存儲在應用(yòng)組件中。

請(qǐng)避免将應用(yòng)的入口點(如 activity、Service 和(hé)廣播接收器)指定爲數據源。相反,您應隻将其與其他(tā)組件協調,以檢索與該入口點相關的數據子集。每個應用(yòng)組件存在的時(shí)間都很(hěn)短暫,具體取決于用(yòng)戶與其設備的交互情況以及系統當前的整體運行狀況。

減少對(duì) Android 類的依賴。

您的應用(yòng)組件應該是唯一依賴于 Android 框架 SDK API(例如 Context 或 Toast)的類。将應用(yòng)中的其他(tā)類與這(zhè)些(xiē)類分離開(kāi)來(lái)有助于改善可測試性,并減少應用(yòng)中的耦合。

在應用(yòng)的各個模塊之間設定明(míng)确定義的職責界限。

例如,請(qǐng)勿在代碼庫中将從(cóng)網絡加載數據的代碼散布到(dào)多個類或軟件包中。同樣,也(yě)不要将不相關的職責(如數據緩存和(hé)數據綁定)定義到(dào)同一個類中。遵循推薦的應用(yòng)架構可以幫助您解決此問題。

盡量少公開(kāi)每個模塊中的代碼。

例如,請(qǐng)勿試圖創建從(cóng)模塊提供内部實現(xiàn)細節的快(kuài)捷方式。短期内,您可能(néng)會(huì)省點時(shí)間,但(dàn)随着代碼庫的不斷發展,您可能(néng)會(huì)反複陷入技術上(shàng)的麻煩。

專注于應用(yòng)的獨特核心,以使其從(cóng)其他(tā)應用(yòng)中脫穎而出。

不要一次又一次地編寫相同的樣闆代碼,這(zhè)是在做無用(yòng)功。 相反,您應将時(shí)間和(hé)精力集中放(fàng)在能(néng)讓應用(yòng)與衆不同的方面上(shàng),并讓 Jetpack 庫以及建議(yì)的其他(tā)庫處理(lǐ)重複的樣闆。

考慮如何使應用(yòng)的每個部分可獨立測試。

例如,如果使用(yòng)明(míng)确定義的 API 從(cóng)網絡獲取數據,将會(huì)更容易測試在本地數據庫中保留該數據的模塊。如果您将這(zhè)兩個模塊的邏輯混放(fàng)在一處,或将網絡代碼分散在整個代碼庫中,那麽即便能(néng)夠進行有效測試,難度也(yě)會(huì)大(dà)很(hěn)多。

類型負責其并發政策。

如果某種類型正在執行長時(shí)間運行的阻塞工(gōng)作(zuò),則應負責将該計(jì)算(suàn)移至正确的線程。該特定類型知(zhī)道(dào)它正在執行的計(jì)算(suàn)類型及其應在哪個線程中執行。類型應該具有主線程安全性,這(zhè)意味着,您可以安全地從(cóng)主線程調用(yòng)這(zhè)些(xiē)類型而不會(huì)阻塞。

保留盡可能(néng)多的相關數據和(hé)最新數據。

這(zhè)樣,即使用(yòng)戶的設備處于離線模式,他(tā)們也(yě)可以使用(yòng)您應用(yòng)的功能(néng)。請(qǐng)記住,并非所有用(yòng)戶都能(néng)享受到(dào)穩定的高(gāo)速連接 - 即使有時(shí)可以使用(yòng),在比較擁擠的地方網絡信号也(yě)可能(néng)不佳。

架構的優勢

在應用(yòng)中實現(xiàn)良好(hǎo)的架構會(huì)爲項目和(hé)工(gōng)程團隊帶來(lái)諸多好(hǎo)處:

提高(gāo)整個應用(yòng)的可維護性、質量和(hé)穩健性。

允許應用(yòng)擴縮。盡可能(néng)減少代碼沖突,使更多人和(hé)更多團隊可以爲同一代碼庫做貢獻。

有助于新手上(shàng)手。架構能(néng)使您的項目保持一緻性,讓團隊中的新成員可以快(kuài)速上(shàng)手,并在更短時(shí)間内提高(gāo)效率。

更易于測試。良好(hǎo)的架構鼓勵使用(yòng)更簡單的類型,這(zhè)些(xiē)類型通常更易于測試。

可以使用(yòng)明(míng)确定義的流程有條理(lǐ)地調查 bug。

在架構方面的投入也(yě)會(huì)對(duì)您的用(yòng)戶産生直接積極影響。用(yòng)戶能(néng)從(cóng)更穩定的應用(yòng)中獲益;同時(shí),由于工(gōng)程團隊效率提高(gāo),用(yòng)戶還可以享受更多功能(néng)。但(dàn)是,架構也(yě)需要前期時(shí)間投入。建議(yì)您閱讀這(zhè)些(xiē)案例研究,了(le)解其他(tā)公司在應用(yòng)中使用(yòng)良好(hǎo)架構的成功案例,這(zhè)有助于您向所在公司解釋前期時(shí)間投入的必要性。

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:Android界面層