咱们使用 gradle 的时候,会使用implementation
, compile
等方式加入一些依赖,好比,aar 是个最经典的例子。那么 aar 到底通过 gradle 怎样的处理使得它能轻松的应用这个产物呢?html
首先,aar
是一个zip
文件,这句话应该不难理解,意思是,aar 是 zip 文件改了后缀名来的,它的二进制格式和 zip 没有啥两样,因此咱们彻底能够把后缀名改为 zip 再解压一下。咱们以androidx.recyclerview:recyclerview:1.0.0
这个 GAV 下过来的 aar 为例子。java
咱们看下解压了 aar 里面的文件是怎么样的 android
R.txt
,
classes.jar
,一个是记录 aar 打包时候生成的
R.java
里面的
id
信息,另一个就是从 java 源代码编译好的 jar 文件(字节码)啦。
经过 聊聊 APK —— 脱离 AS 手工创造 APK 文件 等系列文章咱们能够了解到,apk 的打包过程当中,实际上是不存在 aar 这个文件实体的,那么看完了 aar 的文件目录结构,咱们能够近似得出结论:aar 是一个有特定格式的 zip 文件,且必须解压后才能使用。api
事实上后续的一些验证能证实这个结论。缓存
Gradle 引入依赖咱们可能再熟悉不过了:在Dependencies
这个节点中,加入implementation/api/compileOnly
等指令便可。这些指令在 gradle 的世界中被称之为Configurations
一开始我并不知道为何要用这个名字来命名他们。直到我后来看了 gradle 相关的源码以后,才肯定,一个指令真的是表明了一种配置 —— 你使用implementation
和compileOnly
引入相同的依赖,他们的行为是不一致的,这就是这二者被定义成不一样配置的缘由。至于里面的区别,咱们后续再讲。想提早了解的同窗能够移步官网传送门:docs.gradle.org/current/use…maven
好比咱们引入刚刚的recyclerview
,只用这样就行ide
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.0.0'
}
复制代码
若是你指定的是 maven 仓库的话,其实这里一开始下的一个 pom 文件,pom 文件会指定里面默认产物,你能够自行下载一个过来看一下,地址是:函数
dl.google.com/dl/android/…gradle
咱们从这个 pom 文件里能够看见这个产物的信息,以及它的依赖信息,以及它这些依赖是怎么被引入到项目中的。这个 pom 在“传递依赖”的流程中起了很是重要的做用,若是没有这个 pom ,那么咱们就没有办法进行传递依赖了,这也很好的解释了如下的代码为何是没有传递依赖的:ui
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.0.0@aar'
}
复制代码
由于你若是这样声明的话,gradle 会直接去寻找 dl.google.com/dl/android/… 这个地址而不是 pom 文件的地址。
gradle 对声明依赖的下载使用的是 lazy load 的策略,在你真正获取这个依赖的时候,gradle 才会开始下载这个依赖,不过 android 是在全部任务开始以前有一个AppPreBuildTask
,它会先把全部的依赖拿出来检查一遍,所以 android 依赖的下载在这个任务开始的时候就开始了
可是这个任务实质上是检查下对 aar 的配置是否正确,好比在这里检查了是否使用了provided/compileOnly
的方式引入 aar,这样作是非法的。可是没有对 aar 进行解包提取里面的产物。有兴趣的人能够本身创建一个 gradle 插件项目,看一看这个类。固然也有更偷懒的方式,好比在 点击下载 一个源码包,而后解压,找到相应的类便可。
什么是产物的转换(Artifact Transform)呢?这个很好理解。如今,你从 maven 仓库里面下过来是一个 aar(zip)包,可是我想要里面的classes.jar
拿出来编译,这怎么办到呢?那么咱们须要一个从aar
到classes.jar
的转换。这个过程就被称为产物转换。
产物的转换涉及的问题其实比较多,我可能使用修改文章或者新开文章的方式尽力把这一节的内容讲的更明白一点。
transformer 有两个版本,v1和v2,其实差异不大,不过 v1 已经被弃用了,可是至少在最新的版本中(agp 3.5.1)仍是能使用的,咱们来看一个最重要的类:com.android.build.gradle.internal.dependency.AarTransform
这幅图其实很好理解,就是把一个文件转成一个文件列表,这个动做若是用「解压」两个字解释你们就懂了。通常来讲,咱们注册 transformer 很简单,只要告诉 gradle,个人 transformer 能接受什么属性的文件,能生产什么样属性的文件就能够了。这个「属性」是复合属性,能够是文件后缀名,以及其余认为定义的一些属性。咱们来看下注册的地方:
这里的AarTransform.getTransformTargets()
是一个 aar 转换后产物类型的集合,那么一个 for 循环就是把 aar 里面全部的文件解压到特定的目录,而后把文件路径再返回给 gradle,后续 gradle 在拉取特定类型的文件(好比 TYPE_CLASSES 文件)的时候,就能直接找到转换后的文件了(classes.jar),转换后的结果会缓存,通常路径是 ~/.gradle/caches/transforms-2 或者 ~/.gradle/caches/transforms-1
咱们同时注意到转换器注册中有两句代码
reg.getFrom().attribute(ARTIFACT_FORMAT, EXPLODED_AAR.getType());
reg.getTo().attribute(ARTIFACT_FORMAT, transformTarget.getType());
复制代码
这个文件格式转换以后须要定义的附带属性转换,咱们要提取某个特定属性(格式)的产物的时候,gradle 会根据注册的这个from
和to
的组合,一直找到下载的 aar (或者其余依赖)为止
前面的几节咱们讲了 aar 的文件结构,引入 aar,下载 aar,并从 aar 里面解压(转换)出咱们要的最终产物。那么咱们如今怎么拿到最终产物呢?好比咱们须要获取全部 aar 里面的classes.jar
用于编译最终项目里的 java 源码,那么,咱们须要获取全部依赖里面的classes.jar
。咱们就以代码为例吧。
首先须要用到的类是:ConfigurationContainer, 这个类可使用 project.getConfigurations() 拿到。而后调用ConfigurationContainer#getByName()
获取到指定 Configuration 的依赖列表。一开始咱们已经定义了 Configuration 了,那么咱们能够获取implementation
为例:
project.getConfigurations().getByName("implementation")
这样就获取到全部以 implementation 配置的相关信息了。这样咱们拿到了一个Configuration
,调用getIncoming()
方法,由于是外部依赖,因此是往内传,那么getOutgoing()
就是这个项目的产物了,这个咱们之后再讲。咱们这时候拿到了ResolvableDependencies
对象,注意单词Resolvable
意思是:可解析的。也就是说这个依赖尚未解析完成,只有调用了相关函数才能解析。而后调用这个函数的artifactView()
方法,这是传入一个视图,咱们能够这样理解视图:
咱们知道一开始使用 implementation 'xxxx'
下载来的产物是一个 aar,那么这时候我要的是 aar 里面的 classes.jar 用来编译 java 源代码,所以咱们须要配置一个视图把 aar 里面别的产物过滤掉。这个 ArtifactView
就是这样的概念。回到AarTransform
里面的switch
流程,咱们找到了case JAR
的结点,这个 JAR 就是咱们须要配置视图用的东西了,咱们把完整代码列出来:
void showArtifactJars() {
Configuration implementation = project.getConfigurations().getByName("implementation");
ResolvableDependencies resolvableDeps = implementation.getIncoming();
ArtifactView view = resolvableDeps.artifactView(conf ->
conf.attributes(
attr -> attr.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.JAR.type); //配置 view 过滤的属性,只须要jar
);
);
// 如下这一步通常在 Task Execution 阶段作,上面的这些在 Task Configuration 阶段配置
Set<File> jars = view.getFiles().getFiles();
//......
}
复制代码
最后咱们调用view.getFiles().getFiles();
便可获取到这些 jar 文件,固然若是调用view.getArtifacts().getArtifacts();
你还能获取到这个 jar 原先是属于坐标依赖(或者 project 依赖等)的一些信息。
属性过滤文件的信息咱们之后再讲,若是有兴趣的朋友能够先看官方文档,这篇文档值得细读:
欢迎关注个人公众号「TalkWithMobile」