天天看點

安居客 Android 項目架構演進

閱讀本文大概需要 6.66 分鐘。

導讀: 這篇文章是我的一位好友,現擔任安居客 android team leader 的張磊所寫,記錄了安居客 android 端這幾年來的架構遷移與變化,非常值得看,也很有借鑒意義,是以這裡推薦給大家,并已獲獨家授權釋出,感興趣的可以在文末關注作者的 github 與知乎專欄。

入職安居客三年從工程師到 team leader,見證了 android 團隊一路走來的發展曆程。是以有心将這些記錄下來與大家分享,也算是對自己三年來一部分工作的總結。希望對大家有所幫助,更希望能得到大家寶貴的建議。

1三網合并

三年前入職時安居客在業務上剛完成了三網合并(新房、二手房、好租和商業地産多個平台多個網站合成現在的 anjuke.com,這在公司的曆史上稱之為三網合并),是以移動端也将原先的新房、二手房、好租和商業地産多個 app 合并成為了現在的安居客 app。所謂的合并也差不多就是将多個項目的代碼拷貝到了一起組成了新的 anjuke project。下面這張圖能更加直覺的呈現當時的狀況:

安居客 Android 項目架構演進

這一時期代碼結構混亂、層次不清,各業務技術方案不統一,備援代碼充斥項目的各個角落;甚至連基本的包結構也是胡亂不堪,項目架構更是無從談起。大家隻不過是不停地往上堆砌代碼添加新功能罷了。于是我進入公司的第一件事就是向 leader 申請梳理了整個項目的結構。

而後随着項目的疊代,我們不斷引入了 retrofit、universalimageloader、okhttp、butterknife 等一系列成熟的開源庫,同時我們也開發了自己的 ui 元件庫 uicomponent、基礎工具庫 commonutils、基于第三方地圖封裝的 mapsdk、即時聊天子產品 chatlibrary 等等。這之後安居客項目架構大緻演變成了由基礎元件層、業務元件層和業務層組成的三層架構。如下圖:

安居客 Android 項目架構演進

其中業務層是一種非标準的 mvc 架構,activity 和 fragment 承擔了 view 和 controller 的職責:

安居客 Android 項目架構演進

前面這種分層的架構本身是沒太大問題的,即使到了現在我們的業務項目也已然是基于這種分層的架構來建構的,隻不過在不斷的疊代中我們做了些許調整(分層架構後面在介紹元件化和子產品化的時候會詳細介紹)。但是随着業務的不斷疊代,我們慢慢發現業務層這種非标準的 mvc 架構帶來了種種影響團隊開發效率的問題:

activity 和 fragment 越來越多的同時承擔了 controller 和 view 的職責,導緻他們變得及其臃腫且難以維護;

由于 controller 和 view 的揉合,導緻單元測試起來很困難;

回調嵌套太多,面對負責業務時的代碼邏輯不清晰,難以了解且不利于後期維護;

各層次子產品之間職責不清晰等等

鑒于三網合并時期我還未加入安居客,是以對這一塊的了解難免有偏差,如果有安居客的老同僚發現文章中的描述有不對的地方還望批評指正。

2由 rxjava 驅動的 mvp 架構

一種技術架構無法滿足所有的業務項目,更不可能有一種架構方案能夠一勞永逸。正如上一節中提到的随着業務的不斷疊代,現有架構的缺陷逐漸浮出水面,項目架構必需不斷更新疊代才能更好地服務于業務。

2.1 mvp 的設計與實作

在研究了 google 推出的基于 mvp 架構的 demo 後,我們發現 mvp 架構能解決現在所面臨過的很多問題,于是我們學習并引入到了我們的項目中來,并針對性的做了部分調整。下圖呈現的是安居客 mvp 方案:

安居客 Android 項目架構演進

以前面提到的三層架構的方案來看是這樣的:

安居客 Android 項目架構演進
基于此架構我在 github 上開源了一個項目 minimalistweather (https://github.com/baronz88/minimalistweather),有興趣的小夥伴可以去 clone 下來看看,如果覺得對你有幫助就給個 star 吧。  :)

view layer: 隻負責 ui 的繪制呈現,包含 fragment 和一些自定義的 ui 元件,view 層需要實作 viewinterface 接口。activity 在項目中不再負責 view 的職責,僅僅是一個全局的控制者,負責建立 view 和 presenter 的執行個體;

model layer: 負責檢索、存儲、操作資料,包括來自網絡、資料庫、磁盤檔案和 sharedpreferences 的資料;

presenter layer: 作為 view layer 和 module layer 的之間的紐帶,它從 model 層中擷取資料,然後調用 view 的接口去控制 view;

contract: 我們參照 google 的 demo 加入契約類 contract 來統一管理 view 和 presenter 的接口,使得某一功能子產品的接口能更加直覺的呈現出來,這樣做是有利于後期維護的。

另外這套mvp架構還為我們帶來了一個額外的好處:我們有了足夠明确的開發規範和标準。細緻到了每一個類應該放到哪個包下,哪個類具體應該負責什麼職責等等。這對于我們的 code review、接手他人的功能子產品等都提供了極大的便利。前面提到的 minimalistweather 就是為了定規範定标準而開發的。

這一時期我們還在項目中引入了 rxjava,很好的解決了前面提到的嵌套回調的問題,同時能夠幫助我們簡化複雜業務場景下的代碼邏輯(當然 rxjava 的好處遠遠不止這麼一點,對 rxjava 不了解的同學可以去翻翻我之前「一系列關于 rxjava 的文章」(https://zhuanlan.zhihu.com/p/20687178))。我們也将網絡庫更新到了 retrofit2 + okhttp3,它們和 rxjava 之間能更好的配合。

2.2 mvp 帶來的新問題及解決方案

是不是更新到了 mvp 架構就高枕無憂了呢?很明顯不是這樣!mvp 架構也會帶來以下新的問題:

由于大量的業務邏輯處理轉移到了 presenter 層,在一些複雜的業務場景中 presenter 同樣會變得臃腫難懂。細心的同學可能注意到了前面的架構圖中的 model 層有個 data repository 子產品,data repository 在這裡有兩個作用:一是可以将原本由 presenter 處理的部分邏輯轉移到這裡來處理,包括資料的校驗、部分單純隻與資料相關的邏輯等等,向 presenter 屏蔽資料處理細節,比如作為 presenter 就不必關心 model 層傳遞過來的資料到底是來至網絡還是來至資料庫還是來至本地檔案等等;二是我們引入了 rxjava,但是隻有網絡層中的 retrofit 能傳回 observable 對象,其他子產品都是傳回的還是一些非 observable 的 java 對象,為了能在整個 presenter 層中都體驗 rxjava 帶來的美妙之處,是以可以通過 data repository 做一層轉換;

現在的 mvp 架構中最重的部分就是 model layer 了,這一點從前面的架構圖中就能展現。是以這就要求我們在 model 層的設計過程中職責劃分要足夠清晰,分包更明确,耦合度更低。至于分包大家可以參考 minimalistweather 的方案:db 包為資料庫子產品、http 包為網絡子產品、preference 包是對 sharedpreferences 的一些封裝、repository 包就是前面提到的 data repository 子產品;

同時還有一點需要注意,很多人在使用 rxjava 的過程中往往忘記了對生命周期的管理,這很容易造成記憶體洩露。minimalistweather 中采用了 compositesubscription 來管理,你也可以使用 rxlifecycle 這類開源庫來管理生命周期。

3元件化與子產品化

去年下半年我們 android 團隊内部成立了技術小組,基礎元件的開發是技術小組很重要的一部分工作,是以元件化是我們正在做的事;子產品化更多的是現有的方案受到來自業務上的挑戰以及受到了 oasis feng 在 mdcc 上的分享和整個大環境的啟發,現在正處于設計規劃和 demo 開發的階段。

3.1 元件化

元件化不是個新概念,通俗的講元件化就是基于可重用的目的,将一個大的軟體系統拆分成一個個獨立元件。

元件化的帶來的好處不言而喻:

避免重複造輪子,節省開發維護成本;

降低項目複雜性,提升開發效率;

多個團隊公用同一個元件,在一定層度上確定了技術方案的統一性。

現在的安居客有是三個業務團隊:安居客使用者 app、經紀人 app、集客家 app。為了避免各個業務團隊重複造輪子,團隊中也需要有一定的技術沉澱,是以元件化是必須的。從本篇的第一節大家就能看到元件化的影子,隻不過在這之前我們做的并不好。現在我們需要提供更多的、職能單一、性能更優的元件供業務團隊使用。根據業務相關性,我們将這些元件分為:基礎元件和業務元件。後面在介紹子產品化的時候會有進一步的描述。

3.2 子產品化

自從 oasis feng 在去年的 mdcc 2016 上分享了子產品化的經驗後,子產品化在 android 社群越來越多的被提起。我們自然也不落俗的去做了一些研究和探索。安居客現在面臨很多問題:例如全量編譯時間太長(我這台 13 款的 macbook pro 上打一次包得花十多分鐘);例如新房、二手房、租房等等子產品間耦合嚴重,不利于多團隊并行開發測試;另外在17年初公司重新将租房 app 撿起推廣,單獨讓人來開發維護一個三年前的項目并不劃算,是以我們希望能直接從現在的安居客使用者端中拆分出租房子產品作為一個單獨的 app 釋出上線。這樣看來子產品化似乎是一個不錯的選擇。

是以我們做子產品化的目的大緻是這樣的:

業務子產品間解耦

單個業務子產品單獨編譯打包,加快編譯速度

多團隊間并行開發、測試

解決好租app需要單獨維護的問題,降低研發成本

15年 「trinea」 還在安居客的時候開發了一套插件化架構,但受限于當時的團隊規模并且插件化對整個項目的改造太大,是以在安居客團隊中插件化并未實施下來。而子產品化其實是個很好的過渡方案,将項目按照子產品拆分後各業務子產品間解耦的問題不存在了,後續如有必要,再進行插件化改造隻不過是水到渠成的事。

來看看安居客使用者 app 的子產品化設計圖:

安居客 Android 項目架構演進

整個項目分為三層,從下往上分别是:

basic component layer: 基礎元件層,顧名思義就是一些基礎元件,包含了各種開源庫以及和業務無關的各種自研工具庫;

business component layer: 業務元件層,這一層的所有元件都是業務相關的,例如上圖中的支付元件 anjukepay、資料模拟元件 datasimulator 等等;

business module layer: 業務 module 層,在 android studio 中每塊業務對應一個單獨的 module。例如安居客使用者 app 我們就可以拆分成新房 module、二手房 module、im module 等等,每個單獨的 business module 都必須準遵守前面提到的 mvp 架構。

同時針對子產品化我們也需要定義一些自己的遊戲規則:

對于 business module layer,各業務子產品之間的通訊跳轉采用路由架構 router 來實作(可能會采用成熟的開源庫,也可能會選擇重複造輪子);

對于 business component layer,單一業務元件隻能對應某一項具體的業務,對于有個性化需求的對外部提供接口讓調用方定制;

合理控制各元件和各業務子產品的拆分粒度,太小的公有子產品不足以構成單獨元件或者子產品的,我們先放到類似于 commonbusiness 的元件中,在後期不斷的重構疊代中視情況進行進一步的拆分(這一點的靈感來源于 trinea 的文章);

上層的公有的業務或者功能子產品可以逐漸下放到下層,合理把握好度就好;

各 layer 間嚴禁反向依賴,橫向依賴關系由各業務 leader 和技術小組商讨決定。

對于子產品化項目,每個單獨的 business module 都可以單獨編譯成 apk。在開發階段需要單獨打包編譯,項目釋出的時候又需要它作為項目的一個 module 來整體編譯打包。簡單的說就是開發時是 application,釋出時是 library。是以需要你在 business module 的 gradle 配置檔案中加入如下代碼:

if (isbuildmodule.toboolean()) {     apply plugin: 'com.android.application' } else {     apply plugin: 'com.android.library' }

如果我們需要把租房子產品打包成一個單獨的租房 app,像下面這樣就好:

安居客 Android 項目架構演進

我們可以把 basic component layer 和 business component layer 放在一起看做是 anjuke sdk,新的業務或者項目隻需要依賴 anjuke sdk 就好(這一點同樣是受到了 trinea 文章的啟發)。甚至我們可以做得更極緻一些,開發一套自己的元件管理平台,業務方可以根據自己的需求選擇自己需要的元件,定制業務專屬的 anjuke sdk。業務端和 anjuke sdk 的關系如下圖所示:

安居客 Android 項目架構演進

最後看看安居客子產品化的整體設計圖:

安居客 Android 項目架構演進

子產品化拆分對于安居客這種比較大型的商業項目而言,由于曆史比較久遠很多代碼都運作五六年了;各個業務互相交叉耦合嚴重,是以實施起來還是有很大難度的。過程中難免會有預料不到的坑,這就需要我們對各個業務有較深的了解同時也要足夠的耐心和細緻。雖然辛苦,但是一旦完成子產品化拆分對整個團隊及公司業務上的幫助是很大的。

以上是我的簡單總結以及對子產品化的一些思考,不足之處還望大家批評指正。後面子產品化的 demo 完善後我會把它放到 github,并再出一篇文章詳細介紹子產品化的設計實作細節。