Android應用架構的發展和實踐

前言

架構是我們組織程序,各個項目組件的一種機制。好的架構兼顧了易用性,靈活性,擴展性和複用性。現代Andorid架構已經不限於單體或者單Module了,逐漸在向着多Module和插件化動態化進行發展。

這裏主要圍繞項目單體應用時的架構,單Module到多Module的演變,以及插件化的未來來說。

單體應用架構

單體應用架構是指我們的項目在單Module時的架構,此時項目一般會劃分一些層級,比如UI層,網絡層,邏輯層。這些層級怎麼進行組織呢?

最開始,大家剛接觸Android開發,統統是MVC。MVC的問題是佈局文件作爲View層,無法承擔任何業務邏輯;UI的邏輯和業務邏輯都寫在Activity層,在項目體量不大的時候這樣完全沒有問題。可是當每個頁面的業務邏輯變成之前的2倍的時候,代碼量也會增長一倍,這大大降低了項目的擴展性和維護性。這個時候可能你會通過抽取重構的方式來緩解這種局面,可是隨着業務增多,不久又會陷入這種局面。

慢慢的我們發現,將Activity層的所有業務邏輯抽出去,只剩下UI相關邏輯。這樣既清晰,又能解決上面的問題,所以MVP出現了。在MVP中,Activity充當UI層,Presenter充當業務邏輯;UI層和邏輯層交互,邏輯層和數據層交互。採用接口回調,或者EventBus的方式讓UI層和邏輯層進行通信。

客戶端開發主要是和UI打交道,最好的一定是MVVM。使用databind,或者LiveData可以替代接口回調和EventBus。關於上面這些內容,我在另一篇文章中有更詳細的說明:juejin.im/post/5c47e1…

MVVM架構下的單體應用項目,應該有這樣幾個層級:

  • 數據層,專門負責數據的存取,方式不限(網絡數據,File數據或者KV存儲)

  • VM層,專門負責執行業務邏輯和數據的處理,與數據層有交互

  • UI層,負責顯示每個界面,彈窗等,與VM層有交互

如下圖示:

單Module到多Module演變

這個演變非常像後臺應用的單體架構到微服務架構的演變,適用於公司內部有多個產品需要同時迭代的場景。隨着單體應用向多Module的演變,原先的各個組件都獨立到不同的Module中去了。

大概有兩個階段:

  1. 單一入口Module + 公共類庫Module

  2. 多入口Module + 多公共業務Module + 公共類庫Module

第一個階段很好理解,也是一個自然而然的演變。因爲項目中很多公共的模塊,比如日誌模塊,網絡模塊,數據模塊。這些模塊與邏輯無關,可以給任何項目使用,所以單獨開一個Module或者傳到maven庫。如果要開發新的項目,那也只是將公共類庫Module移植過去即可。

所以,第一階段的項目Module結構大概是這樣:

  • App Module,負責提供項目入口,完成各個業務邏輯功能

  • Library Module,負責提供項目公共組件,比如日誌組件,網絡組件,存儲組件

  • 其他三方庫Module

以抖音App爲例:


上面的階段能做到功能模塊的重用,但是沒有涉及到業務邏輯的重用。新項目也有登錄註冊功能,難道要重新寫一遍麼?

你可能擔心兩個項目的登錄註冊邏輯能一樣麼,界面也不可能一樣啊。界面肯定是不一樣的,但是登錄註冊邏輯大部分是一樣的。這就說明我們其實可以對一些公共業務劃分Module,對於不一樣的地方,完全可以動態化。但是公共業務不能劃入類庫Module中,因爲通用性不夠;還要注意業務Module的抽離本着只抽業務,不抽UI的原則。

當業務Module和類庫Module抽離之後,不同的項目我們可以添加不同的入口Module,入口Module更多是完成入口功能,和UI定製化功能,以及完成一部分差異化的實在不能共用的業務邏輯。

所以,第二階段的項目Module結構大概是這樣:

  • App1 Module,負責提供App1的入口,完成App1的所有UI邏輯和部分不能共用的業務邏輯

  • App2 Module,負責提供App2的入口,完成App1的所有UI邏輯和部分不能共用的業務邏輯

  • Login Module,負責提供登錄註冊功能

  • Video Module,負責提供視頻相關功能

  • Library Module,負責提供公共模塊功能

  • 其他三方Module

客戶端開發中,UI的變數最大,上面的Module劃分儘量將變化少的東西複用,將變化大的東西剝離出來。

他們之間的調用關係應該符合這樣的規則:

  • App入口Module可以調用業務Module和Library Module;

  • 業務Module可以調用Library Module,業務Module之間不能相互調用,也但不能調用App入口Module,目的是保持單向調用流程

  • Library Module只能被調用。

以抖音App爲例:


如果你發現業務Module需要調用App入口Module,或者其他情況,可能你的Module架構設計有問題。

Module間通信

其實上面的架構不存在Module之間的通信,也不需要。

假設這樣一個場景: 在App1中的登錄界面,調用的Login模塊的登錄邏輯,登錄完成後需要跳轉用戶信息界面。

上面的場景只需直接調用Login模塊的邏輯即可,即便發生了不同UI跳轉,也不需要Module通信,因爲所有的UI都寫在App1中了。

如果當初將登錄相關的UI也抽到Login模塊中,看起來封裝度更高,其實有很多不方便。在上面的場景中,需要從Login模塊的UI調用其他模塊的UI,這樣調用流程變得複雜,而且必須要ARouter這樣的通信工具;問題是Login的UI在各個App中不太可能一樣,如果一樣,那麼則可以抽進去。

插件化和熱修復

很多大廠的App功能實在是太多,如果一開始就將所有功能都納入App中,體積下不來,問題是有一些功能很多人從來也不用。

所以從使用程度和重要性程度可以劃分等級,對於那些次要的功能和模塊進行動態化。一開始App只提供部分重要和必須的功能,並且提供一個class運行容器。當用戶需要哪些功能是就進行下載,下載下來的無非是一些class和資源,通過定製的類加載器來加載,並做好生命週期管理和資源與Context的統一,這就是大部分插件化框架的原理。

插件化對於大部分中小公司是不需要的,可以根據需要採納。

熱修復是指將新功能或者Fixed Bug快速更新到用戶的App中,而不用重新下載App。大致原理是先計算出含有新功能的Apk和舊Apk之間的差異,得到一個diff包,一般很小。然後用戶直接下載diff包,App中預先內置了class動態替換的框架,將diff包加載進來就實現了動態熱修復。目前熱修復存在一些兼容性問題。

無論是插件化還是熱修復,不建議自研,大廠有過豐富的經驗和實踐,可直接使用目前成熟的類庫。

App Bundle

一般來說,我們開發的App,裏面包含了各種dpi的資源,兼容各種CPU架構的so庫,這大大增加了Apk的體積。可用戶事實上只需要匹配他手機的一種dpi和一種so庫就足夠,其他的對於用戶來說都是冗餘,上面講的插件化解決了在功能方面的冗餘問題。

能不能將所有的資源,so庫等也按需下載安裝呢?谷歌在AS3.2中提供的App Bundle功能就是幹這個的。之前用戶需要下載所有的資源,下載只會下載和自己設備匹配的資源,大大提高了產品的交付速度和交付質量,可是目前只支持Google Play。


App Bundle會將我們Apk的所有東西打爲壓縮包上傳到Google Play。而Google Play會在用戶下載的時候,根據設備特性生成優化過的Apk給用戶安裝。

App Bundle也支持動態apk,支持將暫時不需要的功能配置爲動態功能,在用戶需要的時候下載,也就是上面的插件化的功能。

App Bundle應該是插件化和動態化的未來,從工程建設的角度幫開發者完成了產品的動態交付。也許,在不久的將來國內商店也會支持這種功能。

最後

每種架構都用自己的優缺點,不能盲目採用多體的架構,也不能偷懶就永遠不升級架構;最終採用什麼樣的架構要根據自己項目體量和發展來判斷。

根據我的經驗,對於大部分公司來說,多體架構的初級階段就完全夠用了。對於同一個團隊需要同時迭代多個項目的場景,可能需要發展到多體架構的第二階段。對於海量功能的App,可能需要插件化。

這些只是我個人的見解和經驗,希望大家踊躍討論,交流一下你們的寶貴經驗,互相提高下!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章