在组件化中,为了避免相同层级的模块之间交叉依赖,这个时候就可以把模块之间相互调用的方法抽象成接口下沉到公共模块,但是这也导致了一个问题,我们对公共模块进行依赖的时候,可能只使用了其中的一两个接口,那这样的话既导致公共模块的臃肿,也增加了编译的时间。所以微信团队在《微信Android模块化架构重构实践》给出了API化这个方案。
思路
1、将工程里想要暴露出去的接口类是Java文件,则将后缀名.Java改成.api,如果是kt文件,则将后缀名.kt改成.kapi。
2、然后利用脚本自动生成一个xxx-api模块。然后供其他的模块进行调用。调用的方式可以根据我前一篇文章Android组件化开发之SPI。
脚本代码如下:
gradle.projectsLoaded {
// 自动为module引入生成的module-api
gradle.rootProject.allprojects.forEach { Project p ->
p.afterEvaluate {
def apiProject = gradle.rootProject.allprojects.find {
it.name.endsWith("${p.name}-api")
}
if (apiProject != null) {
p.dependencies.add("api", apiProject)
}
}
}
}
//自定义include
ext.includeWithApi = { String moduleName ->
include(moduleName)
String originDir = project(moduleName).projectDir
String targetDir = "${originDir}-api"
//新模块的路径
def sdkName = "${project(moduleName).name}-api"
// 每次编译删除之前的文件
deleteDir(targetDir)
//复制.api文件到新的路径
copy() {
from originDir
into targetDir
exclude '**/build/'
exclude '**/res/'
include '**/*.api'
include '**/*.kapi'
include 'api.gradle'
}
def build = new File(targetDir + "/api.gradle")
if (build.exists()) {
build.renameTo(new File(targetDir + "/build.gradle"))
}
// 重命名.api文件,生成正常的.java文件
renameApiFiles(targetDir, '.api', '.java')
renameApiFiles(targetDir, '.kapi', '.kt')
//删除空文件夹
deleteEmptyDir(new File(targetDir))
//正常加载新的模块
include /*prefixName +*/ "$sdkName"
}
private void deleteEmptyDir(File dir) {
if (dir.isDirectory()) {
File[] fs = dir.listFiles();
if (fs != null && fs.length > 0) {
for (int i = 0; i < fs.length; i++) {
File tmpFile = fs[i];
if (tmpFile.isDirectory()) {
deleteEmptyDir(tmpFile);
}
if (tmpFile.isDirectory() && tmpFile.listFiles().length <= 0) {
tmpFile.delete()
}
}
}
if (dir.isDirectory() && dir.listFiles().length == 0) {
dir.delete()
}
}
}
private void deleteDir(String targetDir) {
FileTree targetFiles = fileTree(targetDir)
targetFiles.exclude "*.iml"
targetFiles.each { File file ->
file.delete()
}
}
private def renameApiFiles(root_dir, String suffix, String replace) {
FileTree files = fileTree(root_dir).include("**/*$suffix")
files.each {
File file ->
file.renameTo(new File(file.absolutePath.replace(suffix, replace)))
}
}