前言
隨著公司業務發展,Android 客戶端程式碼量逐漸增多,使用一個工程管理所有程式碼的模式存在程式碼臃腫、編譯時間過長等問題,需要對 Android 客戶端專案進行元件化。
基礎方案
基於團隊規模較小且產品迭代開發較快的現狀,為了快速實現模組化並且上線,對於模組化制定的標準比較簡單:剝離各業務和基礎元件程式碼,且保持彼此獨立。
程式碼剝離過程為劃分業務模組、基礎業務模組、基礎元件三部分;業務模組劃分主要根據公司的產品線進行,劃分粒度較大,包括普通投資產品、基金兩大業務模組;基礎業務模組涵蓋各業務模組共用的業務邏輯,包括分享模組、H5互動模組、使用者模組等;基礎元件劃分主要為了提取與業務無關的基礎元件,包括網路請求、自定義控制元件、基礎工具類等。最後劃分出的模組架構如下圖:
其中業務模組除了產品線模組,還包括主頁面和登入/註冊;主頁面模組主要包括啟動頁、主場景的幾大 Tab 頁面;登入/註冊模組包括使用者登入、註冊、修改密碼等系列頁面,由於內容較獨立且使用場景較廣泛,均被提取成獨立模組。
從依賴角度分析,業務模組屬於基礎業務模組的上層專案,基礎業務模組屬於基礎元件的上層專案,上層專案可依賴下層專案,反過來則不允許。幾大業務模組之間保持獨立,不允許被其他業務模組直接引用;基礎業務模組和基礎元件均可看作普通的 library,被上層專案直接引用。
每一個模組/元件均被提取為獨立專案,程式碼也提交至不同的 git repository 進行管理。
技術框架
基於以上方案將專案程式碼剝離成各模組之後,整體實現機制為:
-
除主模組仍然為 application module,其他模組/元件均為 library module,生成 aar 並 publish 至 maven 倉庫;
-
業務模組通過生成路由配置檔案,使用路由機制互相呼叫各業務模組的功能;
-
專案引用基礎業務模組和基礎元件時,直接在依賴中加入對應的aar配置即可。
業務模組之間採用路由機制的目的為保持業務模組之間的絕對獨立和解耦,路由機制大致描述為:
-
業務模組生成對應的路由配置檔案,並儲存至各專案的 assets 目錄中;
-
業務模組採用路由配置檔案中的配置,通過反射調起其他業務模組中的功能。
路由配置檔案生成
該步驟主要使用APT工具,實現路由配置檔案的自動生成,檔案格式採用 properties 格式。
首先,定義各業務模組路由檔案;新建名為 RouterFilte r的 Annotation,每個業務模組新建一個 Router 類,使用 RouterFilter 註解並賦值;Router 類中的方法為該業務模組暴露給其他模組的方法,實現頁面跳轉等功能。
1 @RouterFilter("main")
2 public class MainRouter extends BaseRouter {
3 @RouterAction("some")
4 public void gotoSomeActivity(Context context, Bundle bundle) {
5 }
7 }複製程式碼
其次,自定義 APT 採用的 processor;建立 Processor 類,對專案中帶有 RouterFilter註解的類進行處理,將 RouterFilter 註解的值和 Router 類的類名一一對應地儲存在properties 檔案中,生成的 properties 檔案命名一般為:router_package_name.properties,properties 檔案內容格式如下:
1 main=package.name.MainRouter複製程式碼
最後,由於 APT 自動生成的 properties 檔案在 build/generated/source/apt/… 目錄中,需要在生成 apk 或者 aar 之前將該目錄作為 assets 路徑的一部分。如果模組是application 專案,則在 gradle 配置檔案中新增如下配置:
1 android.applicationVariants.all { variant ->
2 //這一步保證dofirst永遠執行
3 variant.preBuild.outputs.upToDateWhen { false }
4 variant.preBuild.doFirst {
5 String productFlavor = variant.flavorName
6 android.sourceSets.debug.assets.srcDirs = ["assets", "build/generated/source/apt/${productFlavor}/debug/router"]
7 android.sourceSets.release.assets.srcDirs = ["assets", "build/generated/source/apt/${productFlavor}/release/router"]
8 }
9 }複製程式碼
如果模組是 library 專案,可新增如下較簡單的配置:
1 sourceSets {
2 main {
3 android.sourceSets.debug.assets.srcDirs = ["assets", "build/generated/source/apt/debug/router"]
4 android.sourceSets.release.assets.srcDirs = ["assets", "build/generated/source/apt/release/router"]
5 }
6 }複製程式碼
調起其他業務模組功能
根據以上路由檔案配置生成的步驟,在最後打包的 apk 中,各業務模組生成的路由properties 配置檔案便都彙總在 apk 的 assets 目錄中,所有業務模組均可讀取到其他業務模組的配置檔案。調起其他業務模組功能的機制較簡單,只需提供 RouterFilter 註解的 value 和 RouterAction 註解的 value ;通過 RouterFilter 註解的 value 和 properties 配置檔案可獲取 Router 類的全類名,通過 RouterAction 註解的 value 可獲取Router 類中的 Method 資訊,採用反射即可呼叫該方法,從而調起指定頁面或者實現其他功能。
輔助工具
模組化工作中除了實現上述技術框架,還需要實現很多便於開發的輔助性工具。
自定義 gradle 外掛
模組化導致專案增多,每個專案都存在基本的版本配置資訊,比如 minSdkVersion,targetSdkVersion 等;為了統一配置和方便修改,自定義 config 外掛儲存配置資訊,在每個專案中使用外掛中的變數而非具體的值。 config 外掛中基本配置資訊為:
1 project.customConfig.compileSdkVersion = 26
2 project.customConfig.minSdkVersion = 17
3 project.customConfig.targetSdkVersion = 26
4 project.customConfig.supportLibVersion = `26.1.0`複製程式碼
專案中使用配置資訊如下:
1 apply plugin: `xxx.xxx.xxx.config`
2 ...
3 android {
4 compileSdkVersion project.customConfig.compileSdkVersion
6 defaultConfig {
7 minSdkVersion project.customConfig.minSdkVersion
8 targetSdkVersion project.customConfig.targetSdkVersion
9 ...
10 }
11 ...
12 }複製程式碼
另外,每個 library 專案都需要 publish 到遠端 maven 的操作,每個專案都進行 maven publish 的配置十分繁瑣;同樣地,自定義 publish 外掛封裝所有 publish 的配置引數,在各專案中只需 apply plugin: `xxx.xxx.publish` 使用外掛即可。
多專案管理的指令碼
由於模組化專案屬於不同git repository,需要批量操作多git專案;目前git多專案操作方式主要包括submodule,repo等。由於我們的模組化多專案之間彼此獨立,而且同一個專案需要切換多分支,不同專案所處分支也不一定相同,因此目前多專案管理採用自定義指令碼的方式。已實現所有git基本批量操作的指令碼,並效仿repo封裝成簡單命令模式,便於使用。
在模組化實現之後的開發過程中,還有許多問題均需要批量指令碼,比如某個庫修改了版本號,為了防止存在相容性問題,其他所有專案中依賴此庫的版本號最好都提升為新版本號,該工作便依賴於指令碼完成。
jenkins自動化
在團隊開發過程中,每個成員都可能對同一個 library 進行修改,為了保證遠端 maven庫中 library aar 的版本是最新的,通過 jenkins 自動化實現了每個 library 模組專案push 到 gitlab 之後自動觸發 maven publish 任務。在 jenkins 配置中,所有模組化專案共享同一個 jenkins job,通過 webhook 的引數即可動態獲取當前 push 專案的 git ssh url 以及 branch 等。
實踐總結
目前模組化穩定使用了一段時間,可大致總結出如下優點:
-
由於不同模組/元件存在上下層級,可一定程度對程式碼進行解耦;
-
降低了主專案的編譯時間;
-
可提取越來越多的基礎元件,供公司的其他 Android 專案使用;
-
業務模組剝離,便於應對產品線的下線;
-
模組/元件開發過程可比較獨立,在各自工程中進行除錯即可,不需要在主工程除錯。
當然由於專案數量增多,開發過程相對於單專案操作存在以下缺點:
-
模組/元件開發完成後,需要執行 publish 到本地或者 publish 到遠端的操作才能使用最新的模組/元件邏輯;當然也可以在 settings.gradle 中配置其他專案作為 module,便無需進行 publish 操作;
-
app 打包時需要確保使用的模組/元件均是最新的,步驟沒有單專案模式時簡單。
後續工作中除了優化以上開發過程中遇到的缺點以及當前技術框架,還需對模組進行不斷梳理,劃分較細的模組可以合併,而隨著業務發展逐漸龐大的模組則應該細分,提高整體專案的靈活度。
作者簡介
布恩,銅板街 Android 開發工程師,2016年4月加入團隊,目前主要負責 APP 端 Android 日常開發。
更多精彩內容,請掃碼關注 “銅板街技術” 微信公眾號。