推薦閱讀:
在將業務進行模塊化時,避免不了模塊頁面路由和模塊通信, 大多數我們會用到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
參考資料:
如果您對博主的更新內容持續感興趣,請關注公眾號!