内容介绍
- 多模块依赖打包成aar方法介绍
- 多模块依赖打包成jar的方法介绍
- 使用过程中踩到的一些坑
- 总结
1. 多模块依赖打包成aar方法介绍
在你工程里某个模块添加的插件为library的时候,编译生成的产物就是aar包(其实就是个压缩包里面装了资源文件跟jar还有些配置文件,直接解压就能看到)。
在studio编aar的时候,只会将你的当前模块的内容打包,尽管你依赖了其他模块,也不会将其他模块的东西打进去(远程依赖除外,因为远程依赖指向是一个远程地址,只要指向对了,就能在使用aar的时候被找到)。
既然studio没有为我们准备打包多个模块的功能,就会有大佬去做这样的功能。下面介绍一个插件 fat-aar.gradle
插件地址:https://github.com/adwiv/android-fat-aar/blob/master/fat-aar.gradle
这个插件提供了一个新的依赖方式:embedded (嵌套)
下面介绍下使用方法:
首先在模块的build.gradle里添加插件
本地添加:apply from: ‘fat-aar.gradle’
远程添加:apply from: ‘https://raw.githubusercontent.com/adwiv/android-fat-aar/master/fat-aar.gradle’
接着使用embedded 依赖其他模块
embedded project(’:modelA’)
这样打包的时候,就会帮你把embedded的模块的内容打包进去了。
这种嵌套依赖引发的问题
兼容性问题。目前fat-aar最新版本只支持到gradle 2.3.3版本。因为更高的gradle版本build的产物目录有所变更。fat-aar实现逻辑其实就是帮你把其他模块生成制build目录下*.class以及资源拷贝到需要打包的模块,然后再进行打包。所以要是产物目录变了,就会出问
2.多模块依赖打包成jar的方法介绍
jar包打包起来比aar简单得多,因为不需要到资源文件。只需要将所有class文件打包在一起就行了。而且gradle也提供有打jar的task type。所以这里使用自定义task来做。不过需要注意的是,打包需要排除部分文件,不如R文件、BuildConfig文件。
栗子:
modelB依赖了modelA,现在需要将模块modelB打包成一个jar,并且产生jar的路径为modelB文件目录下的output文件夹。
//执行顺序
//build -> deleteOldTmpDirAndJar-> copyClasses -> makeJavadoc(生成javadoc) -> makeJavadocToJar(打包javadoc) -> assembleJar
//该task作用是删除久的jar包以及久的临时文件
task deleteOldTmpDirAndJar(type: Delete, dependsOn: build) {
println("=============== Delete old ./output ===============")
delete './output'
println("=============== Delete old tmpJarDir ===============")
delete './build/tmpJarDir'
println("=============== Delete old tmpJavaDoc ===============")
delete './build/tmpJavaDoc'
}
//拷贝class文件
task copyClasses(type: Copy, dependsOn: deleteOldTmpDirAndJar) {
println("=============== copyClasses ===============")
from('../modelA/build/intermediates/classes/release')
from('build/intermediates/classes/release')
into('./build/tmpJarDir')
exclude('**/R.class')
exclude('**/BuildConfig.class')
}
//产生javadoc,有需要的同学可以保留
task makeJavadoc(type: Javadoc, dependsOn: copyClasses) {
println("===============make Javadoc ===============")
options.encoding = "UTF-8"
options.charSet = 'UTF-8'
options.setMemberLevel(JavadocMemberLevel.PUBLIC)
source = android.sourceSets.main.java.srcDirs
classpath += project.files('build/tmpJarDir/')
destinationDir = reporting.file("../tmpJavaDoc/")
exclude '**/BuildConfig.java'
exclude '**/R.java'
failOnError false
}
//将javadoc文件目录打成压缩包,有需要的同学可以保留
task makeJavadocToJar(type: Jar, dependsOn: makeJavadoc){
println("=============== Javadoc To Jar ===============")
archiveName = 'modelBJavaDoc.jar'
from('./build/tmpJavaDoc')
destinationDir = file('./output')
}
//将class文件打成jar包
task assembleJar(type: Jar, dependsOn: makeJavadocToJar){
println("=============== assembleJar ===============")
archiveName = 'modelB.jar'
from('./build/tmpJarDir')
destinationDir = file('./output')
exclude('com/xxx/modelA/BuildConfig.class')
exclude('com/xxx/modelB/BuildConfig.class')
}
3. 使用过程中踩到的一些坑
① 使用embedded的坑
如果你使用了modelB使用了embedded方式依赖了medelA。而modelC又依赖了modelB的时候。你会发现,你怎么编译modelC都会报错
Multiple dex files define Lcom/xxx/xxx/xxx
原因是在modelB build了之后fat-aar插件会帮你把modelA的build目录的class文件拷贝到了modelB 的build文件目录里面去。因为你依赖了modelA的关系,studio在编译过程中也会去modelA的build文件目录去找class文件。所以导致了modelA的build目录的class跟modelB里面的重复了。
“实际上,fat-aar的embedded其实是compile的一个封装”
那上面说的这个问题怎么解决呢,我这里提供一个方法供参考 。要是大家有什么更好的方法,也可以分享下。
我这边的需求是上传maven仓库的时候才用到embedded的依赖,其他情况都使用compile。所以我通过gradle执行的task来区分依赖的类型
def dependType = 'compile'
gradle.startParameter.taskNames.each({
String taskName = it
if(taskName.contains("uploadArchives")) { //当task是上传maven的时候使用嵌套依赖
dependType = 'embedded'
return
}else{
dependType = 'compile'
}
})
dependencies {
if (dependType.equals("embedded")){
embedded project(':modelA')
} else{
compile project(':modelA')
}
}
后续在使用中遇到什么坑,再补充。未完待续!
总结
使用embedded的依赖进行多模块打包aar的方式,是目前比较多人采用的一种方式。多模块打包aar并不是完全没有其他的方式,比方说使用远程依赖的方式也是可以的,不过比较麻烦。
如果大家觉得我哪里不对或者有疑问的地方,可以在评论区留意,一起交流学习。