在将业务进行模块化时, 避免不了模块页面路由和模块通信, 大多数我们会用到 ARouter,EventBus 三方库. 模块化过程中有一个尴尬的问题摆在面前: Event 事件, Router path 放在哪里? 因为很多业务 module 都需要收发 Event 事件, 进行页面路由, 所以只能将 Event, Router path 下沉到基础库. 这样导致的结果是基础库越来越大, 至多 把 Event 事件, Router path 摆放在独立的 module, 然后基础库依赖这个库, 如下图所示:
我们希望业务模块发送的事件, 注解使用的 Router path 都在模块自己这里定义, 而不是下层到基础库, 当其他 module 需要路由, 事件, 接口就暴露出来. 原理是在编译期将公用接口下沉到基础库同层级, 供其他 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 文件夹改成其他名字, 就没有这个问题了, 是不是很奇怪! 没去究其原因.
最后
来源: http://www.jianshu.com/p/fe431b00dbe7