懶人模式開啟Android模組自動化Api之旅
在將業務進行模組化時,避免不了模組頁面路由和模組通訊, 大多數我們會用到ARouter,EventBus三方庫。 模組化過程中有一個尷尬的問題擺在面前:Event事件、Router path放在哪裡? 因為很多業務module都需要收發Event事件,進行頁面路由,所以只能將Event, Router path下沉到基礎庫。 這樣導致的結果是基礎庫越來越大,至多 把Event事件、Router path擺放在獨立的module,然後基礎庫依賴這個庫,如下圖所示:
我們希望業務模組傳送的事件,註解使用的Router path都在模組自己這裡定義,而不是下層到基礎庫,當其他module需要路由、事件、 介面就暴露出來。關於這點《微信Android模組化架構重構實踐》 也提到了這件事情,並且自創性的使用了一種叫“.api”化的方式來解決這件事情。原理是在編譯期將公用介面下沉到基礎庫同層級, 供其他module使用,而這段程式碼的維護仍然放到非基礎庫中。這種base庫不會膨脹,程式碼維護的責任制更明確,確定挺不錯。如下圖:
在ModuleA,B把XXXBusEvents、XXXRouterParams,暴露的公用介面檔案字尾名以.api (並不要求一定.api後者,只要跟後續的自動Api化外掛或者指令碼一致就行)命名, rebuild之後自動生成ModuleA-api,ModuleB-api 模組,ModuleA,B也會自動新增各自對應 api module依賴。
講完了原理,下面就可以實現,這裡使用ARouter,EventBus,只對Java檔案進行Api化,步驟如下:
新建工程,建立base、moduleA、moduleB 模組在moudleA,moduleB中建立api檔案
預設情況下,Android stuio 是不能識別.api檔案,如果想編輯.api字尾的java檔案, 為了能讓Android Studio繼續高亮該怎麼辦?可以在File Type中把.api作為java檔案型別,操作如下圖:
設定好後,可以在.api檔案中像java檔案一樣愉快擼程式碼了,其他類可以引用.api中的類。
檢視setting.gradle檔案指令碼如下:
1 include ':app', ':base',':modulea',':moduleb'
include 4個module,做個測試,在setting.gradle include test,同步後,test目錄下只有iml檔案, 沒有build.gradle、AndroidManifest.xml等檔案,所以除了拷貝.api檔案到對應目錄並重命名為.java, 還需要額外建立這兩個檔案,這裡我事先在base module中準備了通用module的build.gradle檔案, 拷貝到對應目錄即可,AndroidManifest.xml就拷貝base module目錄下的,指令碼實現如下:
1 def includeWithApi(String moduleName,String baseModuleName) { 2 //先正常載入這個模組 3 include(moduleName) 4 5 //找到這個模組的路徑 6 String originDir = project(moduleName).projectDir 7 //這個是新的路徑 8 String targetDir = "${originDir}-api" 9 //新模組的路徑 10 def sdkName = "${project(moduleName).name}-api" 11 //新模組名字 12 String apiName="${moduleName.substring(1,moduleName.length())}-api" 13 14 15 //這個是公共模組的位置,我預先放了一個 ApiBuildGralde.gradle 檔案進去 16 String apiGradle = project(baseModuleName).projectDir 17 18 // 每次編譯刪除之前的檔案 19 deleteDir(targetDir) 20 21 //複製.api檔案到新的路徑 22 copy() { 23 from originDir 24 into targetDir 25 exclude '**/build/' 26 exclude '**/res/' 27 include '**/*.api' 28 } 29 30 //直接複製公共模組的AndroidManifest檔案到新的路徑,作為該模組的檔案 31 copy() { 32 from "${apiGradle}/src/main/AndroidManifest.xml" 33 into "${targetDir}/src/main/" 34 } 35 36 //file("${targetDir}/src/main/java/com/dhht/${apiName}/").mkdirs() 37 38 //修改AndroidManifest檔案 39 //fileReader("${targetDir}/src/main/AndroidManifest.xml",apiName); 40 41 //複製 gradle檔案到新的路徑,作為該模組的gradle 42 copy() { 43 from "${apiGradle}/ApiBuildGralde.gradle" 44 into "${targetDir}/" 45 } 46 47 //刪除空資料夾 48 deleteEmptyDir(new File(targetDir)) 49 50 //重新命名一下gradle 51 def build = new File(targetDir + "/ApiBuildGralde.gradle") 52 if (build.exists()) { 53 build.renameTo(new File(targetDir + "/build.gradle")) 54 } 55 56 // 重新命名.api檔案,生成正常的.java檔案 57 renameApiFiles(targetDir, '.api', '.java') 58 59 //正常載入新的模組 60 include ":$sdkName" 61 62 }
修改setting.gradle檔案如下:
1 include ':app', ':base' 2 includeWithApi(":modulea",":base") 3 includeWithApi(":moduleb",":base")
rebuild後,就可以看到moduleA-api,moduleB-api,並有對應的java檔案如下圖:
新增moduleA路由到moduleB,moduleB給moduleA傳送事件邏輯,進行打包,會報如下錯誤:
很顯然,ARouter註解處理器無法識別.api檔案,path置為null處理,在moduleA,B新增對應的***-api模組依賴,就可以打包成功了。
奔著偷懶的原則,不想每次手動新增***-api模組依賴,自動動態新增依賴,實現gradle指令碼如下:
1 ext{ 2 //自動新增***-api依賴 3 autoImportApiDependency = {extension -> //extension project物件 4 def children = project.rootProject.childProjects 5 //遍歷所有child project 6 children.each {child -> 7 //判斷 是否同時存在 *** module 和 ***-api module 8 if(child.key.contains("-api") && children.containsKey(child.key.substring(0,child.key.length() - 4))){ 9 print "\n" 10 11 def targetKey = child.key.substring(0,child.key.length() - 4) 12 def targetProject = children[targetKey] 13 14 targetProject.afterEvaluate { 15 16 print '*********************\n' 17 print targetProject.dependencies 18 //通過列印 所有dependencies,推斷需要新增如下兩個依賴 19 targetProject.dependencies.add("implementation",targetProject.dependencies.create(project(":" + child.key))) 20 targetProject.dependencies.add("implementationDependenciesMetadata",targetProject.dependencies.create(project(":" + child.key))) 21 22 //列印 module 新增的依賴 23 targetProject.configurations.each {configuration -> 24 print '\n---------------------------------------\n' 25 configuration.allDependencies.each { dependency -> 26 27 print configuration.name + "--->" +dependency.group + ":" + dependency.name + ":" + dependency.version +'\n' 28 } 29 30 } 31 32 33 34 print '*********************\n' 35 } 36 37 } 38 39 40 } 41 } 42 }
autoImportApiDependency 方法封裝在Config.gradle,在根build.gradle中呼叫:
1 apply from: 'Config.gradle' 2 ext.autoImportApiDependency(this)
可以正常打包,併成功運行了。
遇坑集錦:
1.kotlin整合ARouter,儘管設定了AROUTER_MODULE_NAME,依然報如下錯誤: ARouter::Compiler An exception is encountered, [null] 可以考慮是否是gradle和 kotlin 版本的問題。
2.業務模組moduleA處於整合模式時,即整合到App殼工程中去,也會將單一模組做 成App啟動的原始碼和資源打包apk中,儘管設定了sourceSets,也沒效果。
問題就出在debug資料夾的名字,把debug資料夾改成其他名字,就沒有這個問題了,是不是很奇怪!沒去究其原因。
傳送門:懶人模式開啟Android模組自動化Api之旅github
參考資料:
微信 Android 模組化架構重構實踐(上)
Android實現模組 api 化
美團貓眼電影Android模組化實戰總結
如果您對博主的更新內容持續感興趣,請關注公眾號!