推薦閱讀:
在將業務進行模塊化時,避免不了模塊頁面路由和模塊通信, 大多數我們會用到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文件腳本如下:
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目錄下的,腳本實現如下:
def includeWithApi(String moduleName,String baseModuleName) {
//先正常加載這個模塊
include(moduleName)
//找到這個模塊的路徑
String originDir = project(moduleName).projectDir
//這個是新的路徑
String targetDir = "${originDir}-api"
//新模塊的路徑
def sdkName = "${project(moduleName).name}-api"
//新模塊名字
String apiName="${moduleName.substring(1,moduleName.length())}-api"
//這個是公共模塊的位置,我預先放了一個 ApiBuildGralde.gradle 文件進去
String apiGradle = project(baseModuleName).projectDir
// 每次編譯刪除之前的文件
deleteDir(targetDir)
//複製.api文件到新的路徑
copy() {
from originDir
into targetDir
exclude '**/build/'
exclude '**/res/'
include '**/*.api'
}
//直接複製公共模塊的AndroidManifest文件到新的路徑,作爲該模塊的文件
copy() {
from "${apiGradle}/src/main/AndroidManifest.xml"
into "${targetDir}/src/main/"
}
//file("${targetDir}/src/main/java/com/dhht/${apiName}/").mkdirs()
//修改AndroidManifest文件
//fileReader("${targetDir}/src/main/AndroidManifest.xml",apiName);
//複製 gradle文件到新的路徑,作爲該模塊的gradle
copy() {
from "${apiGradle}/ApiBuildGralde.gradle"
into "${targetDir}/"
}
//刪除空文件夾
deleteEmptyDir(new File(targetDir))
//重命名一下gradle
def build = new File(targetDir + "/ApiBuildGralde.gradle")
if (build.exists()) {
build.renameTo(new File(targetDir + "/build.gradle"))
}
// 重命名.api文件,生成正常的.java文件
renameApiFiles(targetDir, '.api', '.java')
//正常加載新的模塊
include ":$sdkName"
}
修改setting.gradle文件如下
include ':app', ':base'
includeWithApi(":modulea",":base")
includeWithApi(":moduleb",":base")
rebuild後,就可以看到moduleA-api,moduleB-api,並有對應的java文件如下圖:
添加moduleA路由到moduleB,moduleB給moduleA發送事件邏輯,進行打包,會報如下錯誤:
很顯然,ARouter註解處理器無法識別.api文件,path置爲null處理,在moduleA,B添加對應的***-api模塊依賴,就可以打包成功了。
奔着偷懶的原則,不想每次手動添加***-api模塊依賴,自動動態添加依賴,實現gradle腳本如下:
ext{
//自動添加***-api依賴
autoImportApiDependency = {extension -> //extension project對象
def children = project.rootProject.childProjects
//遍歷所有child project
children.each {child ->
//判斷 是否同時存在 *** module 和 ***-api module
if(child.key.contains("-api") && children.containsKey(child.key.substring(0,child.key.length() - 4))){
print "\n"
def targetKey = child.key.substring(0,child.key.length() - 4)
def targetProject = children[targetKey]
targetProject.afterEvaluate {
print '*********************\n'
print targetProject.dependencies
//通過打印 所有dependencies,推斷需要添加如下兩個依賴
targetProject.dependencies.add("implementation",targetProject.dependencies.create(project(":" + child.key)))
targetProject.dependencies.add("implementationDependenciesMetadata",targetProject.dependencies.create(project(":" + child.key)))
//打印 module 添加的依賴
targetProject.configurations.each {configuration ->
print '\n---------------------------------------\n'
configuration.allDependencies.each { dependency ->
print configuration.name + "--->" +dependency.group + ":" + dependency.name + ":" + dependency.version +'\n'
}
}
print '*********************\n'
}
}
}
}
}
autoImportApiDependency 方法封裝在Config.gradle,在根build.gradle中調用:
apply from: 'Config.gradle'
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
參考資料:
如果您對博主的更新內容持續感興趣,請關注公衆號!