應用程式架構指南

本指南包含建構最佳做法和建議架構以用於建構強大的且高品質的應用程式。

行動應用程式使用者體驗

一般Android應用程式含有多個應用程式元件,包括活動片段服務內容供應者廣播接收器。您必須在應用程式資訊清單中宣告大部分的應用程式元件。接著,Android作業系統會根據這個檔案決定如何將您的應用程式整合至裝置的整體使用者體驗。假設一般的Android應用程式可能會包含多個元件,使用者通常在短時間內就會與多個應用程式互動,因此應用程式必須配合各種不同的使用者導向工作流程和工作進行調整。

請注意,行動裝置也會受到資源限制,作業系統隨時可能中斷部分應用程式處理程序,以騰出空間使用新的裝置。

鑑於此環境的條件,您的應用程式元件可能會個別和無序地啟動,並且操作系統或使用者可以隨時將其刪除。由於這些事件不在您的控管之下,您不應在應用程式元件中儲存或保留任何應用程式資料或狀態,而且應用程式元件不應該彼此相依。

常見架構原則

如果不要使用應用程式元件來儲存應用程式資料和狀態,應該如何設計應用程式?

隨著Android應用程式的規模不斷擴充,請務必定義架構,讓應用程式能夠調度資源、提升應用程式的穩定性,並讓應用程式更容易進行測試。

應用程式架構會定義應用程式各部分之間的關聯,以及每個部分應負的責任。為滿足上述需求,在設計應用程式架構時,請務必遵循下列特定原則。

區隔疑慮

最重要的原則是區隔注意事項。常見的做法是在ActivityFragment中寫入所有程式碼。這些UI類別應該只包含處理UI和作業系統互動操作的邏輯。因此,請盡可能讓這些類別保持精簡,避免出現與元件生命週期相關的許多問題,並提高這些類別的可測試性。

提醒您,您不需要擁有ActivityFragment的實作,而是只是代表Android OS與應用程式之間合約的黏附類別。作業系統可能會隨時根據使用者互動或系統記憶體等系統狀況(例如記憶體不足),隨時進行刪除。如要提供令人滿意的使用者體驗和更易於管理的應用程式維護體驗,建議您盡可能減少依附元件。

透過資料模型使用雲端硬碟使用者介面

另一個重要原則是,您應將使用者介面從資料模型 (建議使用永久模型)驅動。資料模型代表應用程式的資料。兩者與應用程式中的使用者介面元素和其他元件無關。這表示,並未綁定使用者介和應用程式元件的生命週期,但當作業系統決定從記憶體中移除應用程式的處理程序時,仍會銷毀。

永久模型非常適合下列原因:

  • 如果Android OS刪除您的應用程式以釋出資源,使用者並不會遺失資料。

  • 網路連線不穩定或無法使用時,您的應用程式仍會繼續運作。

如果您的應用程式架構是以資料模型類別為基礎,可讓您的應用程式更具有測試性與穩定性。

本節說明如何按照建議的最佳做法建構應用程式。

考量上一節提到的一般架構原則,每個應用程式至少應包含兩個層:

  • 使用者介面圖層用於在螢幕上顯示應用程式資料。
  • 資料層包含應用程式的商業邏輯,並揭露應用程式資料。

您可以新增名為網域圖層的額外層,藉此簡化和重複使用使用者介面與資料層之間的互動。

在一般應用程式架構中,使用者介面層會從資料層或選用的網域圖層取得應用程式資料(位於使用者介面層和資料層之間)。
圖1典型應用程式架構圖。

使用者介面圖層

使用者介面層(或呈現層)的作用是在螢幕上顯示應用程式資料。只要資料因使用者互動(例如按下按鈕)或外部輸入(例如網路回應)而改變,使用者介面應更新以反映變更。

使用者介面圖層包含兩個部分:

  • 在螢幕上顯示資料的使用者介面元素。您可以使用檢視畫面或Jetpack Compose函式建構這些元素。
  • 狀態持有者(例如ViewModel類別)保存資料、將其公開至使用者介面並處理邏輯。
在一般架構中,使用者介面層的使用者介面元素會取決於狀態持有者,這又取決於資料層或選用網域層的類別。
圖2.使用者介面圖層在應用程式架構中的角色。

如要進一步瞭解這個圖層,請參閱使用者介面圖層頁面

資料層

應用程式的資料層包含商業邏輯。商業邏輯可為應用程式提供價值,且會根據決定應用程式建立、儲存及變更資料的規則組成。

資料層是由存放區組成,每個存放區都可包含零到多個資料來源。您應針對應用程式中處理的各種資料類型建立存放區類別。舉例來說,您可以為電影相關的料建立MoviesRepository類別,或是與付款相關資料相關的PaymentsRepository類別。

在一般架構中,資料層存放區提供了資料給應用程式其餘部分,及取決於資料來源。
圖3.資料層在應用程式架構中的角色。

存放區類別用於下列工作:

  • 將資料公開給應用程式的其餘部分
  • 集中對資料的變更。
  • 解決多個資料來源之間的衝突。
  • 從應用程式的其餘部分摘要資料來源。
  • 包含商業邏輯。

每個資料來源類別都應只負責處理一個資料來源(可能是檔案、網路來源或本機資料庫)。資料來源類別是應用程式與系統作業之間的橋樑。

如要進一步瞭解這個圖層,請參閱資料層頁面

網域圖層

網域圖層是使用者介面與資料層之間的選用圖層。

網域層負責封裝複雜的商業邏輯,或是多個ViewModel重複使用的簡易商業邏輯。這個層並不是強制性做法,因為並非所有應用程式都會有上述需求。建議只在需要時才使用,例如處理複雜度或可重複使用。

包含時,選用的網域層可以為使用者介面層提供依附元件,並取決於資料層。
圖4.網域層在應用程式架構中的角色。

這個圖層中的類別通常稱為用途互通者。每次使用時,都必須對單一功能負責。舉例來說,如果多個ViewModel仰賴時區以在螢幕上顯示適當的訊息,則應用程式可能擁有GetTimeZoneUseCase類別。

如要進一步瞭解這個圖層,請參閱網域圖層頁面

管理元件之間的依附元件

應用程式中的類別需要其他類別才能正常運作。您可以使用下列任一設計模式來收集特定類別的依附元件:

  • 依附元件植入(DI):依附元件植入可讓類別定義其依附元件,而不必建構這些依附元件。在執行階段,另一個類別會負責提供這些依附元件。
  • 服務定位器:服務定位器模式提供了註冊資料庫,可讓類別取得依附元件,而不是建構依附元件。

這些模式可讓您擴充程式碼,因為該格式提供了管理依附元件的明確模式,而不必複製程式碼或增加複雜度。此外,這些模式還可讓您快速切換測試和實際工作環境實作。

我們建議您遵循依附元件植入模式,並在 Android應用程式中使用Hilt程式庫。Hilt會自動追蹤依附元件樹狀結構來建構物件,提供依附元件的編譯時間保證,並建立Android架構類別的依附元件容器。

一般最佳做法

程式設計是廣告素材欄位,建構Android應用程式也不例外。解決問題的方法有很多種;您可能會在多個活動或片段之間傳輸資料、擷取遠端資料,並在本機儲存資料以供離線模式使用,或處理任何不常見的應用程式遇到的任何其他常見情況。

雖然下列建議做法並非必要,但在大多數情況下,這些程式碼能讓您更穩固、長期地測試及維護程式碼:

不要將資料儲存在應用程式元件中。

避免將應用程式的進入點(例如活動、服務和廣播接收器)指定為資料來源。而是應該與其他元件協調,才能擷取與該進入點相關的部分資料。每個應用程式元件都會相對短暫,具體而言取決於使用者如何與裝置互動,以及系統的整體健康狀態。

減少Android類別的依附元件。

您的應用程式元件應成為僅仰賴Android架構SDK API (例如ContextToast)的類別。排除應用程式中的其他類別,可幫助測試及減少應用程式中的組合

在應用程式中建立不同模組之間的明確責任範圍。

例如,請勿將程式碼從網路載入資料的代碼分散到程式碼集的多個類或套件中。同樣地,請避免在同一類別定義多項不相關的工作,例如資料快取和資料繫結。遵循建議的應用程式架構可協助您完成這項作業。

每個單元盡可能不要曝光。

舉例來說,請勿試圖新建一個從模組公開內部實作的快捷方式。雖然短期內可能會出現一些時間,但隨著程式碼基礎的發展,您可能需要支付技術債務。

將重點放在應用程式的獨特核心,讓應用程式脫穎而出。

請勿藉由重複撰寫相同的樣板程式碼來做無謂的重複作業。因此,請將時間與精力投注在應用程式的獨特之處,並讓Jeetpack程式庫和其他推薦的資料庫處理重複的樣板。

考慮讓應用程式的每個部分獨立進行測試。

例如,假設API是針對網路擷取資料的明確定義API,便可更輕鬆地測試在本機資料庫中保留該資料的模組。或者,假如您將這兩個模組的邏輯放在同一個位置,或是將網路程式碼分配給整個程式碼集,那麼這樣的測試將變得困難許多,甚至無法做到。

請盡可能保留相關且最新的資料。

這樣一來,即使裝置處於離線模式,也能享有應用程式的功能性。提醒您,並非所有使用者都能享有穩定快速的高速連線,即使他們使用這些功能,也不會在擁擠場所中收到不良訊號。

範例

以下Google範例展現了良好的應用程式架構。去探索他們,以瞭解實務做法: