前言
專案大了,編譯慢了,開發效率低了,怎麼辦? 也許你已經知道了元件化,但專案迭代任務緊張,根本沒有時間進行整體解耦,更害怕一下子改動太大導致的風險不可控,不敢大改,怎麼辦?
先別急著放棄,漸進式元件化了解一下
背景故事
在實行元件化改造之前,我們對業內的一些技術文章及開源庫進行調研之後,發現基本上千篇一律地都是基於路由這種方案作為通訊引擎來實現元件化,重要的是元件化之前得先解耦原來的專案程式碼。很尷尬,我們沒那麼人力和時間來一下子做這麼一大塊事情。這時候我們是這樣想的:
-
可不可以先不解耦?
這個問題問得好,可是...不先解耦,元件怎麼單獨以app執行呢? 複製程式碼
-
新業務新module可以無耦合,總可以單獨執行除錯了吧?
元件需要登入怎麼辦? 需要跟其它元件通訊怎麼辦? 複製程式碼
-
用URLScheme來跨app通訊總可以了吧?
頁面跳轉還行,雖然有個中轉頁面,僅在開發期間使用勉強也能接受 但如何獲取服務?那可是要查詢介面的實現類,跨app呼叫時行嗎? 複製程式碼
-
將需要呼叫的業務程式碼一起打包總可以了吧?
確實可以,但這些程式碼在哪裡?主app module看一下? 難不成要跟主app一起打包?沒解耦的那些module要全部編譯才行哦 複製程式碼
-
那...有沒有這樣一種方案:讓我們可以立即就在新的業務上用元件化的形式進行開發(單元件module以apk的形式編譯執行除錯,並且可以與其它業務模組互相通訊),享受到元件化編譯速度快、除錯效率高的好處,又不需要馬上解耦專案,而是在迭代過程中利用偶爾出現的碎片空閒時間來一個一個慢慢地解耦呢?
好想法,這就是我們想要的:立即元件化開發 & 漸進式元件化改造 不過很遺憾,好像沒有什麼現成的方案可用。 那我們就自己設計一個吧! 複製程式碼
正式由於專案程式碼耦合度高,解耦困難,而且迭代任務緊張,沒辦法單獨抽出時間來解耦,導致元件化改造一直停留在口頭上。
為了在不影響業務迭代業務開發的前提下也能用元件化的形式來進行開發,我們設計了一個支援立即元件化開發 & 漸進式元件化改造的框架:CC(已開源,點這裡看原始碼)
快速瞭解CC
- CC : Component Caller
- 是一套基於元件匯流排的元件化實施方案
- 一靜一動,開發時執行2個app:
- 靜:主App (通過跨App的方式呼叫單元件App內的元件)
- 動:正在開發中的單元件App (通過跨App的方式呼叫主App內的元件)
- 支援漸進式元件化改造
- 解耦只是過程,而不是前提
用CC來立即開始元件化開發並漸進式地改造你的專案
先了解一下漸進式元件化改造的概念
以i百聯App為例(上海百聯集團旗下的一個電商App)來看一下元件化之前專案中存在的耦合情況:
- 先是一個啟動頁(Splash),裡面會有一些初始化、載入廣告等業務,然後跳轉到主頁,廣告可能跳轉到商品詳情頁、用web模組開啟H5活動頁,存在不少耦合
- 主頁中有:首頁、分類、發現、購物車、我的等Fragment頁面/業務模組,還要處理push等功能,也會存在不少耦合
- 首頁中需要跳轉到商品搜尋,還有各種業務Fragment/業務模組,需要跳轉商品詳情、各種列表頁、各種活動H5頁面,耦合程度非常高
- 很多需要使用者登入的功能模組都需要耦合登入模組
- 商品詳情頁、Web模組等模組幾乎所有的業務模組都會用到,耦合程度不言而喻
- 百聯到家、奧萊代購、分享、埋點、收銀臺、LBS、秒殺等等等等
最終專案的耦合狀態是這個樣子的(一個圓圈代表一個模組,圓圈交叉代表存在耦合情況):
為了能讓大家看得清楚,圖片上僅僅列出了有限的幾個模組,但即使是這樣,我們用一團亂麻來形容它也毫不為過。
我們的工程師一直是在這樣的環境下進行業務迭代開發和bug修復,改程式碼要小心翼翼,生怕引起其它邏輯出bug,最主要的是開發/除錯效率非常低:在使用了maven的情況下,在15吋高配的macbook pro上編譯執行每次都要花3-5分鐘,在16GB記憶體的windows桌上型電腦上更是動輒10-15分鐘,嚴重影響開發效率,元件化勢在必行。
但業務迭代排期時間非常緊張,測試資源不足,我們需要的是:立即元件化開發 & 漸進式元件化改造
在瞭解漸進式元件化改造之前,先來看看與之相對的非漸進式元件化改造
非漸進式的元件化方案要求我們必須在元件化初期立即對專案進行解耦,至少是我們需要被新業務呼叫到的元件需要解耦成元件,否則新業務元件脫離主app單獨執行除錯的時候無法正常工作。
在漸進式元件化的方案中,可以先不用解耦,只需要讓單獨執行的元件能夠呼叫到主App中的功能即可。思路是這樣的:
- 新業務以元件形式開發
- 新元件需要呼叫的主App中的業務,在對應的模組中建立一個元件類,對外暴露對應的服務,供其它元件呼叫,並不需要現在就將這個模組解耦
- 新元件通過跨App的方式呼叫主App中的元件
- 主App也可以通過跨App的方式呼叫到單獨執行的元件App中的元件
- 在同一個module中可以建立多個元件類,將來解耦時將對應的元件類移動到解耦後的module中即可
旁白:
- 接入CC後,我們的專案就已經元件化完成了
- 新業務:會員積分,以元件的形式進行開發:建立一個
IComponent
介面的實現類,對外暴露自身提供的服務,並且可以獨立編譯執行除錯 - 會員積分元件需要用到登入、訂單和Web模組的功能
- 在登入、訂單、Web模組對應的module或包下建立對應的
IComponent
介面實現類,對外暴露自身的服務,讓會員積分元件能夠跨App呼叫到這些元件 - 此時如果我們有時間,可以進行解耦,將相對比較容易解耦的登入模組解耦出來,改造成一個元件
- 這時候又來了新業務:使用者瀏覽記錄,仍然以元件的方式開發
- 瀏覽記錄元件需要能開啟主App的商品詳情頁,在商品詳情頁對應的module或包下建立對應的
IComponent
介面實現類,對外暴露自身的服務,讓瀏覽記錄元件能夠跨App呼叫到商品詳情元件 - 其它的業務也可以用同樣的方式來做
- 利用每個迭代過程中的出現的一些碎片空閒時間,將原來專案中的業務模組逐個從主App中解耦出去變成元件
- 如果遇到有些幾個元件暫時無法完全解耦,但如果作為一個整體可以解耦出來,就像動畫中示例的商品詳情和訂單模組一樣,可以將它們作為一個整體解耦出來(一個module,多個
IComponent
對應多個元件),等將來有時間了再繼續拆分 - 持續地按照這個思路進行解耦,最終目標是將整個專案中的所有業務模組都解耦出來變成元件
CC是怎麼做到這一點的?
首先,CC的核心通訊引擎採用的不是路由方案,而是元件匯流排方案(路由 vs 元件匯流排)
CC採有2套呼叫流程:App內部元件呼叫和跨App呼叫。
框架在接收到元件呼叫請求時,優先檢視當前App內是否有本次CC呼叫指定的元件
- 有: 執行App內部元件呼叫流程
- 沒有:檢查當前app是否開啟跨app呼叫功能:
- 未開啟: 返回-5的狀態碼,代表未找到指定的元件
- 已開啟: 執行跨App元件呼叫流程
採用元件匯流排的方案,在App內部呼叫元件時,等效於直接呼叫IComponent.onCall(cc)
方法,將呼叫方設定的呼叫引數傳遞給元件,元件執行完之後將執行結果返回給呼叫方,這個過程中沒有使用反射,執行效率高
在這個過程中,CC完成了一次呼叫請求的轉發:查詢到元件物件,並將呼叫其onCall方法,將呼叫引數傳送給它,並將元件執行的結果返回給呼叫方
悄悄地告訴你:CC中自帶3種AOP策略,例如動畫中顯示的CustomerInterceptors
就是其中之二:全域性攔截器和針對本次CC呼叫的攔截器。定義一個攔截器也很簡單:實現IGlobalCCInterceptor
介面即可
通過RemoteCCInterceptor
與另一個App的ComponentService
建立連線,將CC中的呼叫引數傳遞給ComponentService
,在ComponentService
中使用這些引數,發起一個App內部的CC呼叫,最終通過LocalCCInterceptor
呼叫到IComponent.onCall(cc)
。
元件的執行結果原路返回給呼叫方
在這個過程中,CC完成了2次呼叫請求轉發:
- 跨App轉發:將呼叫引數轉發給另一個App
- App內部轉發:找到元件物件,呼叫其onCall方法將呼叫引數傳送給它
可以看出,跨App呼叫時:
- 呼叫元件的程式碼與在App內部呼叫時完全一樣,無需任何改動
- 實現元件的程式碼
IComponent.onCall(cc)
方法也是在LocalCCInterceptor
中呼叫,與在App內部呼叫時一樣
總結
本文從我們在實際實施元件化過程中的一些思考入手,引入了漸進式元件化的概念,介紹了用CC來實現立即元件化開發 & 漸進式元件化改造的實施步驟。
並用動畫的方式向大家展示了漸進式元件化與非漸進式元件化的區別以及支撐CC實現漸進式元件化的元件呼叫流程。
本文中的圖片及動畫取自上週末(6月9日)在愛奇藝移動技術沙龍分享時的演講稿,PPT文件可在CC交流群(QQ群:686844583)的群檔案中下載,歡迎加群交流。
點選這裡瞭解關於CC的更多內容