一、目標
本文主要記錄組件化項目的實踐過程及其中的思考。微信
具體實施一項技術項目以前咱們會首先肯定對應的目標,以後的行動計劃都會朝着目標一步步靠攏。網絡
- 大的層面上說就是作到各個模塊可以在開發階段獨立運行,充分解耦;
- 小的層面上說使各個組件(技術、業務組件)更加容易複用;
簡單的總結就是把一個大的Project工程,變成若干個小的Module工程,就是這樣。框架
看到這個圖你們是否是想你要說的原來這麼簡單啊,先把刀都放下好好說話,實際上組件化看起來簡單可是實踐道路確是異常艱難,不信咱們繼續看!工具
二、基礎庫抽取
最初咱們的Project總體只有一個Module,也就是說業務代碼和技術代碼都在一塊兒,經過package的方式區分開來的。這種狀況下作組件化難度是極大的,注意不誇張的說就是極大,由於你們在一塊兒牢牢的抱成一團,相互依賴,然而要想直接拆出來困難重重:首先要把全部的基礎庫抽取出來到單獨的基礎Library工程,而後App這個Module依賴於這個基礎Library工程,發現如下難題:組件化
- 基礎庫可能不按規範不在特定的package下;
- 基礎庫可能和特定業務結合的相對緊密,沒法直接移動;
- 移動基礎庫到單獨的Library使用AS工具可能移動不完整;
- 一個類可能引用了若干個類,幾層引用下來,工做量遠超想象;
所以當我剛剛邁出第一步的時候個人心裏就已是這樣的:ui
安慰本身萬事開頭難嘛,我作了如下事情:url
- 先收集、移動各個不在特定package的基礎類到特定package;
- 將基礎庫和特定業務隔離;
- 整理、精簡基礎類;
而後我就開始當心翼翼移動基礎類到單獨的基礎Library,這個過程也十分虐心,由於利用AS的Move功能並不必定保險,強烈建議:設計
- 在單獨的分支作組件抽離,作好版本控制,頻繁備份,隨時還原,以防某一個類找不到致使的Build失敗導致須要從頭開始(必定要注意);
- Move一次,及時Build一次,能夠及時發現那個類沒有Move完整;
這樣你就覺得解決了全部問題?Naive!!版本控制
- 部分基礎庫,例如網絡請求,涉及地方、關聯的類實在太多,屢次的Move功能使用也沒有徹底將其移動完畢,Move完畢以後各類Build失敗;
當時個人表情是這樣的:cdn
實在難以一次Move完畢,因而我換了一種思路:Move基礎庫的緣由是爲了讓新建的別的業務Module使用,也就是Library中必須存在這些基礎類,那我直接在Library中建立出來不就好了嗎?
- 將難以抽離的基礎類使用Rename功能從新命名,而後Copy了一份到Library中;
- 以後將模塊移出來的時候一定找不到以前的基礎類,咱們將報錯的地方改到如今的引用;
對於難以移出的基礎類咱們項目確實是這麼作的,效率明顯更高!
三、路由的設計
3.1 爲何須要路由
明確一個問題:各個業務模塊之間不會是相互隔離而是必然存在一些交互的;
- 在Module A須要跳轉到Module B某界面,而咱們通常都是使用強引用的Class顯式的調用;
- 在Module A須要調用Module B提供的方法,例如別的Module調用用戶模塊退出登陸的方法;
這兩種調用形式你們很容易明白,正常開發中你們也是絕不猶豫的調用。可是咱們在組件化開發的時候卻有很大的問題:
- 模塊B的Activity Class在本身的Module B,那Module A必然引用不到,顯式跳轉行不通;
- 同理,直接去調用某個Module的方法也行不通;
由此:必然一種支持組件化的交互方式,這種交互方式須要支持UI跳轉以及方法調用的能力,同時處理好多參數及不一樣參數類型。
3.2 方式之使用事件通知
備註:此處事件通知指代EventBus或者廣播。
這種思路很好想到,在須要交互的地方發通知,而後接收方根據不一樣的通知類型作出不一樣的處理。相信各位老司機不須要代碼也能直接明白。
優勢:
缺點:
- 隨着交互的增多須要定義的事件實體類爆炸;
- 不方便找調用方與接收方;
備註:EventBus只有當業務模塊在真實的線上運行階段是在本身單獨的進程纔不可用,而這種場景對絕大數App徹底夠用。
推薦星級:二星級
3.3 方式之一個固定的方法
實現方案:在Activity或者須要暴露的普通類中聲明一個統一的方法,這個方法本身去實現,對Activity來講是去實現UI跳轉;對普通類來講則是去實現功能調用。可是這種方式須要解決兩個問題:
- 對於跨Module類引用不到:首先須要確認的是跨Module的類確定是引用不到的,那麼咱們就給這些類打上標記,間接的就能知道相應的類;標記的形式能夠是一個Url,對於Url確定是不區分Module的。例如:我給ActivityA打上一個標記"activitya",而後把這個url做爲key,這個類Class做爲value使用HashMap存儲起來,那麼我在別的Module就能直接經過相應的url來獲取想要調用的類,而後調用這個特定的方法便可。
- 方法簽名不固定的問題:這個很好理解,我要作不一樣的事情那須要的參數無論是個數仍是類型確定是不同的,可是這樣的話顯然沒法作到調用一個固定的方法。這時候咱們仍然能夠選擇曲線救國:咱們只傳遞一個參數進去,而這個參數則是一個HashMap,好處則是,能夠傳遞任意個數、類型的參數。這樣調用方法的時候你能夠將隨意多個數、類型的參數傳遞進去,而後在方法內從HashMap中取出真正須要的參數。
缺點:
- 侵入性太強,任何須要被調用的地方都須要按照統一的格式進行改造;
- 實現極其不友好,若是我須要和別的Module通訊,那我須要詳細的知道傳遞的參數個數及類型,可是這種實現方式沒法明確的像平時方法調用那樣被IDE給提示出來;
推薦星級:強烈不推薦,經得起時間檢驗的方案纔是可行的好方案,而這種方式是經不起規模化推廣考驗的。
3.4 方式之真正的路由
以上兩種方式雖然均可以解決問題,可是坦白講,若是實際用到了項目裏的話推動會是極爲困難的一件事,由於體驗實在是太差了!一個容易被推動、使用體驗好的路由應該具有使用方便、上手成本低,改形成本小等基本素質,那麼分攤下來應該具體體如今這幾點上:
- 針對UI跳轉:
- 改形成本低,不爲跳轉再重寫方法;
- 全部參數類型均支持傳遞;
- 針對Module間交互:
- 能夠清晰的知道方法簽名,直接被IDE提示,和普通的方法調用沒有區別,調用者一目瞭然;
來看下實際的解決方案:
- 對於Activity,咱們也是給它打上一個標記,一個Activity對應一個Url,而後處理好參數的傳遞問題便可。
- 對於Module間調用,咱們在Library工程中建立出每一個Module須要向外提供能力的接口,而後每一個Module本身去實現對應的實現類;而且也使用HashMap將這個接口與實現類進行保存,這樣在別的Module就能夠根據在Library中存在的接口獲取到真正的實現類,而方法調用的時候就是簡單的調用一個對象的方法,IDE提示很友好,並且不限制方法簽名哦。
推薦星級:強烈推薦!!備註:具體的路由實現以後會有專門的文章。
四、業務組件的剝離
在路由的侵入達到必定程度以後就要作業務組件的剝離,須要注意幾點:
4.1 先決條件
- Library庫抽離或者準備完畢;
- 路由框架侵入要靠前;
這兩項屬於基礎設施,不能邊開展邊作業務組件的剝離,否則必定會萬分痛苦:各類報錯,各類Build不過影響工做。
4.2 業務剝離的準則
首先須要明確對於不一樣的項目、要求以及不一樣的資源分配,業務剝離的程度也是不同的。
- 最好按照產品功能進行劃分,由於自己就是相互之間就有關聯,並且代碼也可能在同一個package下,方便一塊兒Move;
- 剝離的顆粒度由粗到細,組件化初期能夠先粗粒度的剝離,快速驗證組件化方案以及踩坑,穩定以後再細粒度拆分;
4.3 共享數據的組件
業務組件實現單獨運行是能夠的,可是實際上不少狀況下本身獨立運行是跑不起來的,舉個例子:大多數業務都會和用戶體系掛鉤,那麼缺少用戶體系的業務組件步履維艱。
那麼比較好的作法就是在技術組件剝離以後,優先把共享數據的組件(例如用戶組件)先剝離出來,而後別的組件須要共享數據的時候就能夠直接依賴於這個組件便可。
再寫下去,本文篇幅就過長不利於吸取了,別的主題咱們下篇文章接着聊!
廣告時間
今日頭條各Android客戶端團隊招人火爆進行中,各個級別和應屆實習生都須要,業務增加快、日活高、挑戰大、待遇給力,各位大佬走過路過千萬不要錯過!
本科以上學歷、非頻繁跳槽(如兩年兩跳),歡迎加個人微信詳聊:KOBE8242011