补齐Android技能树 - 玩转Gradle(二) | 小册免费学(上)

简介: 在开始本节内容前,先来温习下几个关键词:Project(项目) → Gradle的构建 → 由一个或多个Project组成;Task(任务) → Gradle中的Project → 由一个或多个Task组成;Action(执行动作) → Gradle中的Task → 由一个或多个Action(函数/方法)按序组成;

在开始本节内容前,先来温习下几个关键词:


  • Project(项目) → Gradle的构建 → 由一个或多个Project组成;


  • Task(任务) → Gradle中的Project → 由一个或多个Task组成;


  • Action(执行动作) → Gradle中的Task → 由一个或多个Action(函数/方法)按序组成;

然后再康康上一节提到的Gradle构建生命周期:


网络异常,图片无法展示
|


上图中有三处提到了 Script,在Gradle中,他们是 配置脚本,脚本在执行时,实际上是配置了一个特殊类型的对象:


Init Script → Gradle对象、Settings Script → Settings对象、Build Script → Project对象;


这个对象又称脚本的 代理对象,代理对象上的每个属性、方法都可以在脚本中使用。


每个Gradle脚本都实现了Script接口,由0个或多个 脚本语句 (statements)和 脚本块 组成。 脚本语句可以包含:函数调用、属性赋值和本地变量定义,脚本块则是一个方法调用,传入一个 配置闭包,执行时对代理对象进行配置。


本节就来细说下 构建生命周期依赖规则&依赖冲突处理,这部分内容比较枯燥,但我依旧会写得简洁易懂些,希望看完对你解决Android多模块构建问题时可以有所裨益。


0x1、Initialization(初始化)


1. Init Script(初始化脚本)


涉及到的文件及脚本执行顺序如下:

$GRADLE_USER_HOME/init.gradle(.kts)$GRADLE_USER_HOME/init.d/[*.gradle(.kts)]

这一步会生成 Gradle 对象,提供了三类API:获取全局属性、项目配置、生命周期HOOK,部分API如下:


// 获得Gradle实例的方法:在*.gradle文件中调用.gradle 或 Project.getGradle()。
/* ========= ① 获取全局属性 ========= */
gradleHomeDir → 执行此次构建的Gradle目录;
gradleUserHomeDir → Gradle User Home目录;
gradleVersion → 当前Gradle版本;
includedBuilds → 获取内嵌构建;
parent → 获取父构建;
pluginManager → 获取插件管理器实例;
plugins → 获取插件容器;
rootProject → 获取当前构建的根项目;
startParameter → 获取传入当前构建的所有参数
taskGraph → 获取当前构建的task graph,此对象在taskGraph.whenReady { } 后才具有内容
/* ========= ② 项目配置,闭包方法会在Project可用时立即执行 ========= */
rootProject(action) // 为Root Project添加闭包
allprojects(action) // 为所有 Project添加闭包


应用示例:Gradle全局设置Maven仓库,创建一个 $GRADLE_USER_HOME/init.gradle(.kts) 或在 $GRADLE_USER_HOME/init.d/ 目录下随便创建一个xxx.gradle(.ktx)文件,内容如下:


// 项目依赖仓库
allprojects {
    repositories {
        maven { url "https://maven.aliyun.com/repository/google" }
        maven { url "https://maven.aliyun.com/repository/jcenter" }
    }
}
// Gradle脚本依赖仓库
gradle.projectsLoaded {
    rootProject.buildscript {
        repositories {
            maven { url "https://maven.aliyun.com/repository/google" }
            maven { url "https://maven.aliyun.com/repository/jcenter" }
        }
    }
}


配置后,Gradle项目构建时会优先从这里的Maven仓库下载依赖,然后再到项目中配置的仓库中下载,在Gradle编译提速中,可把Maven地址替换为自己搭建的Maven私服,所以Gradle项目编译时都会优先走这里~


2. Settings Script(设置脚本)


涉及文件:项目根目录下的 settings.gradle(.kts),在此文件中:声明参数构建的模块管理构建过程需要的插件,此处会生成一个 Settings 对象。


Gradle会从当前目录开始查找此文件,找到停止找不到则往父目录递归查找,所以建议不管是单项目还是多项目,都要有一个 settings.gradle(.kts) 文件。


① 声明参数构建的模块


Settings类中,最重要的方法就是 include(String… projectPaths) 方法,用于添加参与构建的Project,传入一个 可变参数,值是每个Project的路径( 当前project相对于根project的路径 ),示例如下:


// [:]代表项目分隔符,类似于路径分隔中的[/],以:开头表示相对于根目录
include ':module1'
include ':libs:library1'
// 也可写到一行
include ':module1',':libs:library1'
// 注:当子项目不在根目录下时需使用相对路径描述
project(":module3").projectDir = File(rootDir, "../../library2")
// 默认情况下Gradle会使用根项目所在目录名称作为项目名
// 配合CI一起使用时,往往会检测到一个随机文件名,可以强制指定项目名称
rootProject.name = 'JustTest'


每个被include的项目都会生成 ProjectDescriptor 对象, 用于描述该模块。模块名称最终都会添加到Map类型的 DefaultProjectRegistry.projects 中,所以无需特殊处理include的顺序。


另外,即便settings.gradle(.kts)什么都不写,也会加载当前目录下的Build Script。


② 管理构建过程需要的插件


通过 settings.pluginManagement 的相关接口实现,比如指定插件的仓库地址(默认从Gradle官方创建仓库查找),打开settings.gradle:


pluginManagement {
    // 对应PluginManagementSpec类
    repositories { // 管理Plugin Repository(仓库)
        google { url "https://maven.aliyun.com/repository/gradle-plugin" }
    }
}
rootProject.name = 'temp'
include ':module1',':module2'


利用 resolutionStrategy 接口则可进行插件决策,比如打印一个Kotlin项目用到的插件信息:


resolutionStrategy { 
    eachPlugin {   // 接收一个PluginResolveDetails类型的闭包,通过requestsd可以获得plugin的信息
        println "${requested.id} → ${requested.module} → ${requested.version}"
    }
}


输出结果如下:


网络异常,图片无法展示
|


接着可以根据id替换插件或指定插件版本,示例如下:


resolutionStrategy {
    eachPlugin {   // 接收一个PluginResolveDetails类型的闭包,requested可以获得plugin的信息
        println "${requested.id} → ${requested.module} → ${requested.version}"
  // 替换模块
        if (requested.id.namespace == "org.jetbrains.kotlin") {
        useModule("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:${requested.version}")
        }
    // 统一插件版本
        if (requested.id.id == "org.jetbrains.kotlin.jvm") {
            useVersion("1.3.71")
        }
    }
}


另外,此阶段涉及到的两个生命周期事件:settingsEvaluated() 和 projectLoaded(),前者可以拿到配置完毕的 Setting 对象,后者可以拿到包含项目基础信息的 Project 对象。


3. Build Script(构建脚本)


涉及文件:模块目录下的 build.gradle(.kts),用于配置当前模块的 构建信息,分为:


  • 根目录模块的 Root Build Script (一般是对子模块进行统一的配置,没有太多内容);


  • 子模块的 Module Build Script


多模块的构建流程:Init ScriptSettings ScriptRoot Build Script(单模块没这一步) → Build Script (默认字母序,可通过设置依赖关系干预)


Build Script完成的工作有两个:插件引入属性配置,即对 Project 对象进行进一步的配置,生成Task的有向无环图。


① 插件引入


Gradle自身 并没有提供编译打包的功能,它只是一个 负责定义流程和规则的框架,具体的编译工作都是由 插件 来完成的,比如编译Java用Java插件,编译Kotlin用Kotlin插件。


所以插件到底是什么?→ 答:定义Task,并具体执行这些Task的模板


插件的两种类型:


  • 脚本插件:存在于另一个脚本文件中的一段脚本代码;


  • 二进制插件(编译成字节码):实现Plugin接口,通过编程的方式操作构建过程(项目或Jar包形式);


Gradle会内置一些核心插件,并提供简单名字,如 "java",没在其中的插件则需采用完整名字,如:"org.jetbrains.kotlin:kotlin-gradle-plugin",这个又称插件id,唯一不可重复!引入方式区别如下:


// 内置插件引入
apply plugin: 'kotlin-android'
// 也可以使用plugins,不过有些插件不能指定版本,有些必须指定,要注意!
// 下面这种写法是Kotlin中的中缀表达式,apply→ 是否立即应用插件
plugins {
    id("org.jetbrains.kotlin.jvm") version "1.3.71"
    id("org.jetbrains.kotlin.jvm") version "1.3.71"   apply false
    java
    `build-scan`
}
// 非内置插件引入,会将对应Jar文件放到Gradle的classpath下
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
    }
}


② 属性配置


一旦应用了某个插件,就可以使用此插件提供的DSL进行配置,以此干预模块的构建过程。以Android构建为例:


// 引入android.application插件 → 为Project对象添加一个android{}配置闭包
apply plugin: 'com.android.application'
android {
    compileSdkVersion 29    // 使用API 29编译此模块
    // 编译时的一些配置
    defaultConfig {
        applicationId "com.example.test"
        minSdkVersion 26
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
    }
    // 签名配置
    signingConfigs {
        release {
            storeFile file('test.jks')
            storePassword '123456'
            keyAlias 'test'
            keyPassword '123456'
        }
    }
    // 构建类型配置
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
    // 编译选项配置
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}


除了插件另外引入的属性DSL外,Project对象也提供了很多用于配置构建的DSL,如 dependencies 配置编译依赖项,更多可以点进Project源码中自行查看。


网络异常,图片无法展示
|


另外根目录Build Script还可以使用一个 ext 属性用于Project间的数据共享、统一模块依赖版本。


// 根目录build.gradle配置
ext {
    applicationId = "xxx.xxx.xxx"
    buildToolsVersion = "28.0.3"
    compileSdkVersion = 28
    minSdkVersion = 22
    ...
}
// 子模块build.gradle使用
android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    ...
}


相关文章
|
2月前
|
缓存 Unix Android开发
Android安卓项目调试之Gradle 与 Gradle Wrapper的概念以及常用gradle命令深度详解-优雅草卓伊凡
Android安卓项目调试之Gradle 与 Gradle Wrapper的概念以及常用gradle命令深度详解-优雅草卓伊凡
293 8
|
2月前
|
存储 API Android开发
【02】完整的安卓二次商业实战-配置gradle-构建打包原生安卓项目-调试本地运行模拟器-优雅草伊凡
【02】完整的安卓二次商业实战-配置gradle-构建打包原生安卓项目-调试本地运行模拟器-优雅草伊凡
180 4
【02】完整的安卓二次商业实战-配置gradle-构建打包原生安卓项目-调试本地运行模拟器-优雅草伊凡
|
2月前
|
Java 开发工具 Maven
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
199 6
|
6月前
|
Java Android开发
Android studio中build.gradle文件简单介绍
本文解析了Android项目中build.gradle文件的作用,包括jcenter仓库配置、模块类型定义、包名设置及依赖管理,涵盖本地、库和远程依赖的区别。
610 19
|
Android开发
Android基于gradle task检查各个module之间资源文件冲突情况
Android基于gradle task检查各个module之间资源文件冲突情况
Android基于gradle task检查各个module之间资源文件冲突情况
|
9月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
627 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
9月前
|
缓存 Java 测试技术
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
1142 3
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
|
Android开发
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
|
Java Android开发 Windows
玩转安卓之配置gradle-8.2.1
为安卓开发配置Gradle 8.2.1,包括下载和解压Gradle、配置环境变量、修改配置文件以增加国内镜像,以及在Android Studio中配置Gradle和JDK的过程。
1067 0
玩转安卓之配置gradle-8.2.1
|
Android开发
Android Studio: 解决Gradle sync failed 错误
本文介绍了解决Android Studio中出现的Gradle同步失败错误的步骤,包括从`gradle-wrapper.properties`文件中获取Gradle的下载链接,手动下载Gradle压缩包,并替换默认下载路径中的临时文件,然后重新触发Android Studio的"Try Again"来完成同步。
7232 0
Android Studio: 解决Gradle sync failed 错误

推荐镜像

更多
下一篇
oss云网关配置