Android 项目Gradle 解析
gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME // 下载的Gradle压缩包解压后存储的主目录
distributionPath=wrapper/dists // 相对于distributionBase的解压后的Gradle压缩包的路径。
zipStoreBase=GRADLE_USER_HOME // 同distributionBase,只不过存放的是zip压缩包的
zipStorePath=wrapper/dists // 同distributionPath,只不过存放的是zip压缩包的
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip //Gradle发行版压缩包的下载地址。 其中gradle-6.5-all.zip 是包含源代码的,bin是不包含源代码的
- GRADLE_USER_HOME 默认路径是~/.gradle/, 不建议使用本地maven的m2替代
- 如果启动时,指定参数,使用彼得目录代替GradleUserHome, 后果是每次构建需要重新下载插件和依赖到新的目录。
- 默认情况下,gradle运行时,除了和项目打交道,还有当前项目要构建的全新GradleUserHome 目录,所有jar包都得重新下载。
Gradle命令行
- gradlew -?/-h/-help 使用帮助
- gradlew tasks 查看所有可执行的tasks
- gradlew --refresh-dependencies assemble 强制刷新依赖
- gradlew cBC等价于执行Task cleanBuildCache ,这种通过缩写名快速执行任务
- gradlew :app:dependencies 查找app工程依赖树
Gradle 构建机制
settings.gradle
- 支持多工程构建,使用settings.gradle来配置添加子工程(模块).
- settings 文件在初始化阶段执行,创建settings对象,在执行脚本时调用该对象的方法。
- settings.include(String ... projectPaths):
- 将给定的目录添加到项目构件中,“:app”表示文件相对路径,相当于'./app' 文件夹。
- 多项目架构进行分层,把同层次的子工程放在同一文件夹下便于管理,使用':xxx:yyy'表示。
build.gradle
- build.gradle 是项目构建文件,每个工程都有一个build.gradle 文件
- build.gradle 在配置阶段执行,并创建相应工程的project对象,执行的代码可以直接调用该对象提供的方法或属性。
Gradle 生命周期:
- 初始化: Gradle 支持单项目和多项目构建。在初始化阶段,Gradle确定哪些项目将参与构建,并为每个项目创建Project实例,一般我们不会接触到它。比如解析settings.gradle
- 配置阶段: 解析每个工程的build.gradle 文件,创建要执行的任务子集和确定各种任务之间的关系,并对任务做一些初始化配置。
- 执行阶段: Gradle 根据配置阶段创建和配置的要执行的任务子集,执行任务。
Gradle 执行流程
Gradle 钩子函数
- gradle 在生命周期三个阶段都设置了相应的钩子函数调用。
- 使用钩子函数,处理自定义的构建:
- 初始化阶段:gradle.settingsEvaluated和gradle.projectsLoaded(在settings.gradle中生效)
- 配置阶段:project.beforeEvaluate和project.afterEvaluate; grdale.beforeProject、gradle.afterProject及gradle.taskGraph.whenReady.
- 执行阶段:gradle.taskGraph.beforeTask和gradle.taskGraph.afterTask.
Gradle 构建监听:
gradle 可以设置监听,对各阶段都有相应的回调处理。
- gradle.addProjectEvaluationListener
- gradle.addBuildListener
- gradle.addListener
Configuration阶段
- 解析每个Project中的build.gradle,解析过程中并不会执行各个build.gradle 中的task
- 经过Configuration阶段,Project之间以及内部Task之间的关系就确定了。一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task 之间的依赖关系。所有Project 配置完成后,会有一个回调project.afterEvalutate(),表示所有的模块都已经配置好了。
Task
- task 是gradle 中最小的任务单元,任务之间可以进行复杂的操作( 动态创建任务,多任务之间的依赖调用等),gradle的执行其实就是由各种任务组合来执行,来对项目进行构建的。
- 使用gradlew help 命令,任何gradle项目都有一个该task,可以执行此命令观察task执行的流程是否如预期
- gradlw tasks --all 查看所有任务
DefaultTask
- task 定义的任务其实就是DefaultTask的一种具体实现类的对象。
- 可以使用自定义类继承DefaultTask:
- 在方法上使用@TaskAction 注解,表示任务运行时调用的方法。
- 使用@Input表示对任务的输入参数
- 使用@OutputFile表示任务输出文件。
- 使用inputs,outputs直接设置任务输入/输出项
- 一个任务的输出项可以作为另一个任务的输入项(隐士依赖关系)。
Project
- build.gradle在配置阶段会生成project实例,在build.gradle中直接调用方法或属性,实则是调用当前工程project对象的方法或属性。
- 使用Project 提供的api,在多项目构建设置游刃有余:
- project(':app'){} 指定的project(这里是app)配置
- allprojects{} 所有的project配置
- subprojects{}所有的子project配置
- buildscript{} 此项目配置构建脚本类路径。
属性扩展ext
-
使用ext对任意对象属性进行扩展:与局部变量的区别是别的project可见。
- 对project使用ext进行属性扩展,对所有子project可见。
- 一般在root project中进行ext属性扩展,为子工程提供复用属性,通过rootProject直接访问。
- 任意对象都可以使用ext来添加属性:使用闭包,在闭包中定义扩展属性。直接使用=赋值,添加扩展属性。
- 由谁进行ext调用,就属于谁的扩展属性。
- 在build.gradle 中,默认是当前工程的project对象,所以在build.gradle直接使用"ext=" 或者"ext{}"其实就是给project定义扩展属性。
-
使用gradle.properties 以键值对形式定义属性,所有的project可直接使用。
Gradle 插件
- Gradle插件是提供给gradle构建工具,在编译时使用的依赖项。插件的本质就是对公用的构建业务进行打包,以提供复用。
- Gradle插件分为:脚本插件和二进制插件(实现Plugin的类)
- Gradle插件通过apply 方法引入到工程。
Gradle依赖管理
在大多数情况下,项目都要依赖lib形式的可重用功能,还有很多项目可能被切分成多个单独的子工程来构建模块系统。依赖管理是一种可以让项目自动化的定义、解析,以及使用依赖的技术。Gradle提供了强大的依赖管理支持。
Gradle资源库
在Gradle中存储模块的地方就叫资源库。
- 定义了资源库之后,Gradle就知道怎么样查找和检索模块。资源库有两种方式:本地库和远程库。
- 在运行时,如果相应的任务需要,那么Gradle就需要定位依赖的生命,依赖可能需要从远程库下载,也可以从本地库检索或者在多项目构建中的其他项目。这个过程就叫做依赖解析。
- 一旦解析,解析机制就会存储依赖的底层文件作为本地缓存,之后的构建会重用这些文件,而不用再到远程库下载。
- 模块还能提供一些其他的元数据,元数据是描述模块更详细信息的数据,比如在资源库中的坐标,项目的信息等。(group, name_version)。
添加依赖仓库
- 在build.gradle 中使用allprojects{}对所有的工程进行配置,使用repositories{}添加依赖仓库。
- google(): 添加一个在Google的Maven存储库中查找依赖项的存储库。
- mavenCenteral(): 添加一个在Maven中央存储库中查找依赖项的存储库。
- jcenter(): 添加一个在Bintray的Icenter存储库中查找依赖项的存储库。 NOTE: jcenter 即将关闭,需要迁移到mavenCenteral()
- mavenLocal: 添加一个在本地Maven缓存中查找依赖项的存储库。
- maven{}: 指定某个maven仓库的地址,使用url(path)方法来添加。
- ivy{}: 指定某个ivy仓库地址,使用url(path)方法来添加。
Gradle依赖范围
- 在Gradle构建脚本中开发者可以把依赖定义到不同的范围中,比如编译源码或者执行测试。在Gradle中依赖的范围叫依赖项配置。
- Gradle插件内置了几种方式的依赖项配置。
- 在build.gradle中使用dependencies{}添加依赖项。
自定义依赖项配置
添加自定义依赖项配置,就可以使用该依赖项配置添加依赖:
configurations {
abc {
println "abc"
}
}
// 在dependencies{}中使用:abc 'androidx.core:core-ktx:1.2.0'
implementation
- Gradle 会将依赖项添加到编译类路径,并将依赖项打包到构建输出。不过,当你的模块配置implementation依赖项时,会让Gradle了解你不希望该模块在编译时将该依赖项泄露给其他模块。也就是说,其他模块只有在运行时才能使用该依赖项。
- 使用此依赖项配置代替api可以显著缩短构建时间,因为这样可以减少构建系统需要重新编译的模块数。例如,如果implementation依赖项更改了其API,Gradle只会重新编译该依赖项以及直接依赖于它的模块,大多数应用和测试模块都应该使用此配置。
api
- Gradle会将依赖项添加到编译类路径和构建输出。当一个模块包含api依赖项时,会让Gradle了解该模块要以传递方式将该依赖项导出到其他模块,以便这些模块在运行时和编译时都可以使用该依赖项。
- 此配置使用时应格外注意,只能对你需要以传递方式导出到其他上游消费者的依赖项使用它。这是因为,如果api依赖项更改了其外部API,Gradle会在编译时重新编译所有有权访问该依赖项的模块。因此,拥有大量的api依赖项会显著增加构建时间。除非要将依赖项的API公开给单独的模块,否则库模块应改用implementation依赖项。
annotationProcessor
如需添加对作为注解处理器的库的依赖,你必须使用annotationProcessor 配置将其添加到注解处理器的类路径。这是因为,使用此配置可以将编译类路径与注释处理器类路径分开,从而提高构建性能。
查看模块依赖项:
- gradlew app:dependencies --configuration releaseRuntimeClasspath
- x.x.x(*)该依赖已经有了,将不再重复依赖。
- x.x.x ->x.x.x 该依赖的版本将被箭头所指的版本代替
- x.x.x ->x.x.x(*)该依赖的版本被箭头所指的版本代替,并且该依赖已经有了,不在重复依赖。
解决依赖项冲突:
使用exclude 关键字
dependencies {
implementation('some-library') {
exclude group:'com.example.imgtools', module:'native'
}
}
排除整个group
dependencies{
implementation('some-library'){
exclude group: 'com.example.imgtools'
}
}
使用全部排除
//在configurations{} 中添加下面节点
configurations {
all*.exclude module: "support-fragment"
}
Force 强制指定
//强制指定版本
configurations.all{
resolutionStrategy{
force 'com.android.support:support-fragment:26.1.0'
}
}
AGP(Android Gradle Plugin)
Android Studio 构建系统以Gradle为基础,并且Android Gradle插件添加了几项专用于构建Android应用的功能,虽然Android插件通常会Android Studio的更新步调保持一致,但插件可独立 于Android Studio运行并单独更新。
Android Studio 升级了----》AGP也需要升级----》Gradle 需要升级
resValue
使用resValue 可以为当前的构建产品增加资源文件属性
格式: resValue 'string', 'name', 'value'
注: string 表示资源标签的类型。name 表示资源属性名称,value 表示对应的属性值 name 如果已经存在,就不能进行覆盖。
不同的产品风味可以添加自己的resValue, 如果所有的产品都需要添加,则可以在defaultConfig{}进行添加。
buildConfigField
- 使用buildConfigField 为产品修改BuildConfig中的类型。
- BuildConfig是在产品构建时自动生成的java类,里面存放了一些静态常量,编译后可以直接使用类中的常量。
- buildConfigField 'String', 'fieldName', '"value"' 注意: 字符串需要单引号+双引号
- buildConfigField 'Boolean', 'fieldName', 'true'