首發於微信公衆號:BaronTalk前端
安居客 Android App 距離上次的模塊化/組件化重構已經兩年多了,重構以後很好的支撐了兩年多以來的業務發展。但這個世界老是在向前走的,沒有任何一種架構可以一勞永逸的解決全部問題,外部環境的不斷變化相應的也要求項目架構作出改變,以此來應對環境變化所帶來的挑戰。java
本文分享的就是咱們安居客 App 團隊此次向平臺化轉型的背景、轉型過程當中所面臨的問題、挑戰、咱們的解決方案以及我我的在這個過程當中的收穫和感悟。git
自 18 年以來的市場環境你們都知道,各大公司都在縮減開支、提高組織效率,但願用更少的投入帶來更多的產出,以此來應對經濟下行帶的不肯定性。在這個大背景下,集團在 18 年末作出了一系列人效提高、業務整合的動做。github
拿房產業務舉例:58 App 裏的房產業務以前是由北京的房產團隊開發的,而整個安居客 App 是由我所在的上海安居客團隊開發的。在此次調整以後,北京的房產團隊做爲一條垂直業務線來負責 58 App 和安居客 App 中的租房業務的開發,安居客團隊則繼續負責整個安居客 App(包含新房、二手房、內容、IM 等業務)的開發,同時還要開發 58 App 中二手房、新房、房產大內容等業務。小程序
這樣一次調整給咱們帶來了三大挑戰:後端
面對這些挑戰咱們須要轉變開發思惟、改進項目架構。微信
**一次項目重構和架構升級,不僅是要解決當下的問題,更要爲將來一到兩年的業務發展提供支持。**爲了解決這一系列的問題,咱們聯合 58 無線團隊、58 房產團隊、前端、後端多個團隊的同窗一塊兒發起了「木星計劃」,也開啓了咱們的平臺化轉型之路。網絡
當公司的業務調整政策下來之後,團隊面臨的最急迫的一個選擇是繼續在 58 App 上維護老的房產代碼,迭代新功能;仍是將安居客現有業務搬遷到 58 App 實現一套代碼在兩個 App 裏運行呢?架構
前者咱們須要分一半的人力去開發 58 App,這必然致使團隊的需求消化量減半,業務迭代速度受影響。好處是暫時不用作任何技術上的改造。app
後者須要咱們協調 58 無線、58 房產、先後端等多個團隊一塊兒對 App 作一次「大手術」,同時還須要說服產品團隊容忍改造期間業務迭代速度的放緩。好處是可以一次性將安居客上的房產業務搬遷到 58 App 上,後續咱們只需針對房產業務作一次開發就能跑在兩個 App 上,用一份人力作了兩份的活,開發效率翻倍。
爲長遠計,咱們最終選擇了後者。但是要作到一套代碼雙端運行並不容易,58 App 和安居客 App 隸屬於北京、上海兩個不一樣的團隊開發,你們底層庫不同,技術方案不同,開發模式也不同,要達到咱們的目的必然要費一番功夫。接下來我從總體到局部逐步介紹咱們在平臺化演進過程當中的設計思路、遇到的問題和解決方案
所謂平臺化,就是安居客 App 要做爲一個平臺來對平臺上承載的各類垂直業務提供服務,每一個服務都須要對上層提供標準化的接口來支撐平臺上各種垂直業務的功能。
要作到這一點,在現有的業務體系和代碼體量下雖然工做量巨大,但大的思路確是很清晰簡單的:
針對安居客 App ,咱們須要調整架構,引入一個平臺中間層並針對平臺中間層接口作安居客 App 側的實現,並將垂直業務中本來調用平臺接口的地方改成調用中間層接口。同時將以前的業務組件層細分爲「平臺級組件」和「業務級組件」,全部垂直業務再也不依賴平臺級組件,只依賴平臺中間層和業務級組件,並明確業務代碼遷移的邊界。
同時 58 無線團隊的同窗也改進了他們的架構,和咱們同樣引入了平臺中間層及中間層在 58 App 上的實現,保證安居客業務遷移進來後能正常編譯運行。
計算機領域的任何問題均可以添加一箇中間層來解決。58 App 和安居客 App 做爲兩個不一樣的平臺,對業務層提供的能力是不同的,接口、方法名都是不同的。同一塊業務代碼要跑在兩個不一樣的平臺,必然要引入一箇中間層來抹平差別、屏蔽底層細節,同時對外提供統一的接口供業務層調用。
所以 58 無線團隊、58 房產團隊和咱們安居客團隊三方協商,共同制定了一套平臺中間層 API,而後兩個平臺再針對平臺中間層 API 作具體實現。
爲了便於後期管理、劃清代碼邊界,咱們將平臺中間層服務進一步細化,根據平臺差別性將中間層劃分爲平臺公共服務、安居客平臺特有服務、58 平臺特有服務等。以 Java Package 做爲區分,劃分到不一樣的包結構下。一旦後期某一特有服務變成了公共服務,則將其往平臺公共服務遷移。
在實現上,平臺中間層會提供一系列 Service 接口供垂直業務調用,同時提供一個 PlatFormServiceRegistry 類用來註冊和獲取服務。
public class PlatFormServiceRegistry {
...
/** * 註冊服務 */
private void registService(Class serviceInterface, Class<? extends IService> serviceImpl) {
if (serviceInterface != null && serviceImpl != null ) {
classMap.put(serviceInterface.getName(), serviceImpl);
}
}
/** * 獲取服務 */
private <T> T getService(Class<? extends T> service) {
···
IService instance = serviceImplMap.get(service.getName());
if (null == instance) {
try {
Class<? extends IService> serviceClass = classMap.get(service.getName());
if (serviceClass != null) {
instance = serviceClass.getConstructor().newInstance();
serviceImplMap.put(service.getName(), instance);
}
} catch (Exception e) {
Log.d(TAG, e.toString());
}
}
return (T) instance;
}
}
複製代碼
在使用方式上,平臺方首先要註冊服務
//註冊平臺服務
PlatFormServiceRegistry.registeAppInfoService(AjkAppInfoServiceImpl.class);
複製代碼
業務方在使用服務的時候獲取到對應的 Service 就能夠調用相關方法了。
//使用平臺服務
IAppInfoService appInfoService = PlatFormServiceRegistry.getAppInfoService();
appInfoService.getAppName(context);
複製代碼
安居客 App 因爲歷史緣由,Android 和 iOS 在與 H5 的交互協議上有不少不同(雖而後期統一過 JSBridge 協議,但不少歷史遺留協議仍舊是不一致的),58 App 上的 Native 和 JS 交互協議更是和安居客不一致。爲了解決這些問題咱們須要 Android 和 iOS、58 和 安居客有一個統一的 Native 與 JS 的交互協議;同時爲了兼容歷史協議,讓業務平穩過渡,咱們還須要設計一套過渡方案。
在未實現協議統一的狀況下,一個 H5 頁面要上 58 App 和安居客 App 兩個平臺,須要支持兩套協議,再加上安居客以前 Android、iOS 協議的不一致,一個 H5 頁面最多可能須要支持 4~5 套協議。
爲了實現協議的最終統一,而且讓業務平穩過渡,咱們引入了一套過渡方案。在保留兩個平臺現有協議和 JSBridge SDK 的狀況下,58 無線團隊的同窗設計並開發了一個全新的 HybridSDK,過渡階段三套協議並存,來不及調整的舊業務使用舊協議,新開發及本次要調整的業務使用新協議。
而後隨着業務的迭代,不斷廢棄兩個平臺的自有協議,最終走向統一。
現有 App 內的頁面跳轉要麼是 intent 跳轉,要麼是寫死的路由跳轉。在此次的平臺化改造過程當中,咱們從 58 App 上也學到了不少東西,其中動態路由下發就是咱們學習並引入到安居客 App 中的。
簡單的說就是 App 給各個頁面定義好路由協議,App 在調用 API 的時候返回結果中會包含當前頁面跳轉到下一頁面的路由協議,這就是所謂的動態路由下發。這樣作會帶來三個好處:
咱們此次的平臺化改造,不管對安居客 App 仍是 58 App 來講都是一次「大手術」,術後可否保證線上的穩定性、業務數據不受影響是很是重要的。就拿把安居客業務遷移到 58 App 這件事來講,若是一股腦的用安居客遷移過去的房產業務代碼替代 58 App 內的房產業務代碼,就算咱們在技術上作到了絕對的穩定,也難保業務數據不會受影響。
好在得益於上面提到的動態路由下發方案,咱們能夠作到在少許城市、少許用戶上作灰度,讓這部分人先試用安居客遷移過去的業務,其它用戶繼續使用老的房產業務,數據效果好再逐步加量,直到徹底替代。這樣就能最大限度的下降影響,保證上線後的穩定性。
雖然在上一次的模塊化/組件化改造過程當中咱們對各項垂直業務作了拆分、解耦,可是各業務仍是有不少重疊的業務,因而咱們將這些業務下沉到 CommonBusiness 組件中,同時這個 CommonBusiness 組件裏還包含了一些 App 平臺級別的基礎功能,所以這個 CommonBusiness 組件成了個大而全的東西。
在此次的架構調整中,咱們爲了實現業務的快速平移,將 CommonBusiness 和新房、二手房等幾條垂直業務線的代碼一股腦的遷移進了 58 App,這就直接致使了 58 App 體積的快速膨脹。因而在後期,咱們將 CommonBusiness 按能力拆分紅了多個獨立的組件,而且分爲了「平臺級組件」和「業務級組件」。平臺級組件屬於安居客平臺特有,不隨業務遷移;業務級組件屬於多個垂直業務公用的組件,隨業務代碼一塊兒遷移到 58 App。這一點在前面的架構圖中有體現。
前面介紹中間層的時候提到,還有一部分底層庫暫時沒法統一,現階段是經過引入中間層來解決的。但從長遠來看,整個集團無線體系下,依賴的底層庫仍是要走向統一。就拿分享組件來講,現階段安居客和 58 都是使用本身的 ShareSDK,而後中間層定義了一套分享接口,兩個 App 分別調用本身的 ShareSDK 來實現接口知足業務的分享需求。爲了進一步下降開發成本,避免重複造輪子,後期雙方還須要統一使用同一個 ShareSDK,拋棄中間層。最終整個集團體系下全部的 App 的架構應該以下面這張圖所示:
整個木星計劃下來,我的有不少的收穫和感悟。說實話,此次的技術改造在技術上並無太大的難度,更談不上有什麼「黑科技」。改造能順利落地,更多的對於全局的把控、資源的協調溝通以及各個兄弟團隊的積極配合。因此這裏拋開技術不談,談談個人幾點收穫:規範化、流程化和全局視角。
安居客 Android App 團隊在技術上過往基本都屬於小團隊做戰,不多有跨團隊、跨地域協同開發的經驗,也缺乏和集團其它團隊的交流,所以規範和流程一直是咱們團隊所欠缺的。
在以前的小團隊開發模式下,就這麼一畝三分地,想怎麼玩都行,怎麼方便怎麼來。可是這種方式一旦涉及到跨地域跨團隊的協做時就會遇到瓶頸。
好比北京租房團隊的需求開發完要集成進安居客,按照咱們單純的想法,定個時間點提交代碼給咱們就 OK 了,但實際狀況是這種方式根本行不通。業務方何時開發完、測試完、何時交付給咱們?業務集成的交付標準是什麼?平臺方測試和業務方測試如何配合、如何交接?業務代碼是以源碼方式集成仍是 aar 方式集成等等這些都是問題,須要有一套標準化、流程化的規範來約束各方的行爲,這樣才能保證項目順利上線。
像上面這樣的例子還有不少,我這裏就不一一列舉了。
正所謂**「不謀全局者,不足謀一域」**,本次平臺化改造過程當中個人另一個重大收穫是讓我意識到要以全局視角去看到問題。
前面提到咱們在規範和流程上有不少欠缺,不少問題單從自身沒法解決,這促使我跳出本身的圈子來思考問題。咱們作 App 開發的同窗每每容易把視角侷限於本身負責的一個頁面、一個功能模塊、一個業務,能站在整個 App 的角度看待問題的已經寥寥無幾了,更別提跳出 App 的視角來看待問題。
但缺乏全局視角,不少事情就不能很好的完成。舉個例子,假設你接到了一個優化頁面響應速度的任務,若是你單從 App 的角度入手你會發現能作的頗有限,當你一通操做把 App 的優化點作完了卻發現中臺部門提供的 SDK 方法耗時兩秒,API 返回數據耗時三秒,真的是一頓操做猛如虎,一看結果二百五。
上面提到的這個例子還只是最多見、最基礎的一點。真正的全局視角是要求咱們跳出 App 的限制,去思考整個研發流程的痛點、跨團隊協做上還有哪些優化空間等等,這樣才能真正提高咱們的開發效率、產品性能和用戶體驗。就拿我業餘時間裏一直在作的 APM 項目來講,若是隻從 App 的角度來看,要實現對線上性能數據的採集會涉及到 Gradle Plugin、字節碼、ASM、數據的採集、存儲、上報、跨進程通信等等,每一項都不簡單,每一項須要有必定的技術深度才能作好一個 APM 組件。那麼是否是每一項都作好了,實現了一個優秀的 APM 組件就能解決線上性能問題、提高用戶體驗呢?顯然不是!
若是你站在更高的角度來看,一個單純的 APM 組件並無辦法解決任何性能問題。咱們須要從前期開發階段的開發規範和實現方案、QA 驗收標準、上線後的性能數據採集、數據上報後的聚合、分析和報警、性能問題的處理流程和規範等各個維度來協同處理,全方位的系統的思考每個點,從更高的角度入手才能真正解決線上性能問題。
如今咱們把視角再往上拔高一個層次,站在整個技術團隊的角度來看移動開發。
安居客 App 是市面上一類比較典型的互聯網應用,它包含了基本的業務呈現、即時通信、視頻播放、直播等面向用戶的功能模塊,也包含了數據採集、日誌記錄、網絡通信等用戶看不見的功能模塊,同時還需持續交付平臺、測試平臺等等平臺的支持。
早期,即便在同一家公司這些內容各個 App 都是獨立的,各自本身作一套;
後來慢慢發現這種各自爲戰的模式效率過低,重複造輪子的現象太嚴重,因而有了平臺化的概念。即時通信是一個平臺、視頻直播是一個平臺、日誌系統又是一個平臺,持續交付、測試均是單獨的平臺。各個平臺爲各類 App 提供不同的服務,各個 App 單獨對接各個平臺就能夠了,避免了重複造輪子。
這種平臺化方案也有它本身的問題,經過前面的描述你會發現,基本上來一個新業務就要開發一個新系統,造成一個新平臺,這樣也會給 App 的接入帶來困擾,同時不一樣的平臺天然會有不一樣的團隊,這之間的溝通協做成本是巨大的。因而更進一步把各類分散的平臺統一成一個更大的平臺,統一對各類 App 提供服務。
這就是整個 App 研發體系從蠻荒時代到平臺化,再從平臺到中臺的完整進化史。
這裏說的平臺化和文章標題裏的平臺化不是同一個概念,這裏的平臺化是指一套系統做爲一個平臺爲各類 App、Web、小程序等前臺應用提供服務;而文章標題裏的平臺化是指安居客 App 做爲一個平臺來支持各種房產業務。
那麼如今咱們再來看研發團隊的組織結構,就徹底不同了。請看下圖:
當咱們能站在這樣一個角度看團隊的組織結構、研發流程的時候,不少事就更容易理解了。好比中臺部門推出了一套日誌系統,各個前端團隊要不要替換掉自研的埋點庫,使用中臺部門的服務,個人見解是固然要。讓專業的團隊作專業的事,中臺爲各個前端業務團隊賦能,不管是質量上仍是效率上都會有極大的提高。同時這樣也便於對各業務線的用戶數據、行爲作統一的聚合、分析、報警等等,而後進一步反哺業務。
這也是爲何以前集團 TEG 團隊推出 WMDA(58集團埋點系統)後,咱們要頂住巨大壓力在安居客內部推廣的緣由。
平臺化改造能順利完成並不是咱們一個團隊的功勞,這得益於先後端、產品、測試、58 無線、58 房產等多個團隊積極的配合,就好比前面介紹的不少技術方案都是 58 無線團隊的同窗提出並開發的,所以要在這裏說一聲感謝,咱們從兄弟部門學到了不少。
此次平臺化改造的過程當中涉及了太多的內容,其中每個點都能拿出來單獨寫一篇文章,因爲篇幅限制並不能在文中一一詳述。咱們團隊在平臺化方面的實踐上還缺少足夠的經驗,我的能力也有限,未能將細節很好的一一呈現。若是你們發現文章中的錯誤或者實現方案上的不完美,歡迎在評論區留言交流指正。
若是你喜歡個人文章,就關注下個人公衆號 BaronTalk 、 知乎專欄 或者在 GitHub 上添個 Star 吧!
- 微信公衆號:BaronTalk
- 知乎專欄:zhuanlan.zhihu.com/baron
- GitHub:github.com/BaronZ88