元件化實踐詳解(一)

貌似許亞軍發表於2018-03-22

1、目標

本文主要記錄元件化專案的實踐過程及其中的思考。

具體實施一項技術專案之前我們會首先確定對應的目標,之後的行動計劃都會朝著目標一步步靠攏。

  • 大的層面上說就是做到各個模組能夠在開發階段獨立執行,充分解耦;
  • 小的層面上說使各個元件(技術、業務元件)更加容易複用;

簡單的總結就是把一個大的Project工程,變成若干個小的Module工程,就是這樣。

元件化實踐詳解(一)

看到這個圖大家是不是想你要說的原來這麼簡單啊,先把刀都放下好好說話,實際上元件化看起來簡單但是實踐道路確是異常艱難,不信我們繼續看!

2、基礎庫抽取

最初我們的Project整體只有一個Module,也就是說業務程式碼和技術程式碼都在一起,通過package的方式區分開來的。這種情況下做元件化難度是極大的,注意不誇張的說就是極大,因為大家在一起緊緊的抱成一團,相互依賴,然而要想直接拆出來困難重重:首先要把所有的基礎庫抽取出來到單獨的基礎Library工程,然後App這個Module依賴於這個基礎Library工程,發現以下難題:

  1. 基礎庫可能不按規範不在特定的package下;
  2. 基礎庫可能和特定業務結合的相對緊密,無法直接移動;
  3. 移動基礎庫到單獨的Library使用AS工具可能移動不完整;
  4. 一個類可能引用了若干個類,幾層引用下來,工作量遠超想象;

因此當我剛剛邁出第一步的時候我的內心就已經是這樣的:

元件化實踐詳解(一)

安慰自己萬事開頭難嘛,我做了以下事情:

  1. 先收集、移動各個不在特定package的基礎類到特定package;
  2. 將基礎庫和特定業務隔離;
  3. 整理、精簡基礎類;

然後我就開始小心翼翼移動基礎類到單獨的基礎Library,這個過程也十分虐心,因為利用AS的Move功能並不一定保險,強烈建議

  • 在單獨的分支做元件抽離,做好版本控制,頻繁備份,隨時還原,以防某一個類找不到導致的Build失敗致使需要從頭開始(一定要注意);
  • Move一次,及時Build一次,可以及時發現那個類沒有Move完整;

這樣你就以為解決了所有問題?Naive!!

  • 部分基礎庫,例如網路請求,涉及地方、關聯的類實在太多,多次的Move功能使用也沒有完全將其移動完畢,Move完畢之後各種Build失敗;

當時我的表情是這樣的:

元件化實踐詳解(一)

實在難以一次Move完畢,於是我換了一種思路:Move基礎庫的原因是為了讓新建的別的業務Module使用,也就是Library中必須存在這些基礎類,那我直接在Library中建立出來不就行了嗎?

  • 將難以抽離的基礎類使用Rename功能重新命名,然後Copy了一份到Library中;
  • 之後將模組移出來的時候必定找不到之前的基礎類,我們將報錯的地方改到現在的引用;

對於難以移出的基礎類我們專案確實是這麼做的,效率明顯更高!

3、路由的設計

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類引用不到
  • 方法簽名不固定
  1. 對於跨Module類引用不到:首先需要確認的是跨Module的類肯定是引用不到的,那麼我們就給這些類打上標記,間接的就能知道相應的類;標記的形式可以是一個Url,對於Url肯定是不區分Module的。例如:我給ActivityA打上一個標記"activitya",然後把這個url作為key,這個類Class作為value使用HashMap儲存起來,那麼我在別的Module就能直接通過相應的url來獲取想要呼叫的類,然後呼叫這個特定的方法即可
  2. 方法簽名不固定的問題:這個很好理解,我要做不同的事情那需要的引數不管是個數還是型別肯定是不一樣的,但是這樣的話顯然無法做到呼叫一個固定的方法。這時候我們仍然可以選擇曲線救國:我們只傳遞一個引數進去,而這個引數則是一個HashMap,好處則是,可以傳遞任意個數、型別的引數。這樣呼叫方法的時候你可以將隨意多個數、型別的引數傳遞進去,然後在方法內從HashMap中取出真正需要的引數

缺點:

  • 侵入性太強,任何需要被呼叫的地方都需要按照統一的格式進行改造;
  • 實現極其不友好,如果我需要和別的Module通訊,那我需要詳細的知道傳遞的引數個數及型別,但是這種實現方式無法明確的像平時方法呼叫那樣被IDE給提示出來;

元件化實踐詳解(一)

推薦星級:強烈不推薦,經得起時間檢驗的方案才是可行的好方案,而這種方式是經不起規模化推廣考驗的。

3.4 方式之真正的路由

以上兩種方式雖然都可以解決問題,但是坦白講,如果實際用到了專案裡的話推進會是極為困難的一件事,因為體驗實在是太差了!一個容易被推進、使用體驗好的路由應該具備使用方便、上手成本低,改造成本小等基本素質,那麼分攤下來應該具體體現在這幾點上:

  • 針對UI跳轉
    • 改造成本低,不為跳轉再重寫方法
    • 所有引數型別均支援傳遞
  • 針對Module間互動
    • 可以清晰的知道方法簽名,直接被IDE提示,和普通的方法呼叫沒有區別,呼叫者一目瞭然

來看下實際的解決方案:

  1. 對於Activity,我們也是給它打上一個標記,一個Activity對應一個Url,然後處理好引數的傳遞問題即可
  2. 對於Module間呼叫,我們在Library工程中建立出每個Module需要向外提供能力的介面,然後每個Module自己去實現對應的實現類;並且也使用HashMap將這個介面與實現類進行儲存,這樣在別的Module就可以根據在Library中存在的介面獲取到真正的實現類,而方法呼叫的時候就是簡單的呼叫一個物件的方法,IDE提示很友好,而且不限制方法簽名哦

元件化實踐詳解(一)

推薦星級:強烈推薦!!備註:具體的路由實現之後會有專門的文章

4、業務元件的剝離

在路由的侵入達到一定程度之後就要做業務元件的剝離,需要注意幾點:

4.1 先決條件

  1. Library庫抽離或者準備完畢
  2. 路由框架侵入要靠前

這兩項屬於基礎設施,不能邊開展邊做業務元件的剝離,不然一定會萬分痛苦:各種報錯,各種Build不過影響工作。

4.2 業務剝離的準則

首先需要明確對於不同的專案、要求以及不同的資源分配,業務剝離的程度也是不一樣的。

  • 最好按照產品功能進行劃分,因為本身就是相互之間就有關聯,而且程式碼也可能在同一個package下,方便一起Move
  • 剝離的顆粒度由粗到細,元件化初期可以先粗粒度的剝離,快速驗證元件化方案以及踩坑,穩定之後再細粒度拆分

4.3 共享資料的元件

業務元件實現單獨執行是可以的,但是實際上很多情況下自己獨立執行是跑不起來的,舉個例子:大多數業務都會和使用者體系掛鉤,那麼缺乏使用者體系的業務元件寸步難行

那麼比較好的做法就是在技術元件剝離之後,優先把共享資料的元件(例如使用者元件)先剝離出來,然後別的元件需要共享資料的時候就可以直接依賴於這個元件即可

再寫下去,本文篇幅就過長不利於吸收了,別的主題我們下篇文章接著聊!

廣告時間

今日頭條各Android客戶端團隊招人火爆進行中,各個級別和應屆實習生都需要,業務增長快、日活高、挑戰大、待遇給力,各位大佬走過路過千萬不要錯過!

本科以上學歷、非頻繁跳槽(如兩年兩跳),歡迎加我的微信詳聊:KOBE8242011

歡迎關注

相關文章