@TryLoveCatch
2022-06-14T09:38:13.000000Z
字数 14410
阅读 2676
Android知识体系
看完这一系列,彻底搞懂 Gradle
关于Android Gradle你需要知道这些(1)
关于Android Gradle你需要知道这些(2)
10分钟了解Android项目构建流程
关于Android Gradle你需要知道这些(3)
关于Android Gradle你需要知道这些(4)
刘望舒-Gradle-3篇
深入理解Android之Gradle
你真的了解 Gradle 吗?
groovy-jdk
groovy
Gradle DSL
Android DSL
gradle-wrapper.properties中各属性的含义
gradle wrapper分析
我们要开发 Java 程序,本地需要配置 JDK 环境,要开发 Android 程序,需要配置 SDK 一样,想要借助 Gradle 来构建项目,那么按理说本地也需要配置相关的 Gradle 环境才对。
而我们之所以可以省掉这一步,就是 gradle/wrapper 这个目录下的文件的作用了。
Gradle内置了wrapper task帮助我们自动生成wrapper所需de目录文件,我们在一个项目的根目录下面,执行命令即可:
gradle wrapper
生成的文件如下:
|-gradle
|-wrapper
|-gradle-wrapper.jar
|-gradle-wrapper.properties
|-gradlew
|-gradlew.bat
先来看一下gradle/wrapper的目录结构
gradle-wrapper.propertes //gradle-wrapper 配置文件
gradle-wrapper.jar //gradle-wrapper 核心 jar
来看下gradle-wrapper.properties的内容
#Wed Sep 06 14:36:34 HKT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip
重点说一下这个,gradle-wrapper.propertes里面最重要的就是这一句,指明了当前这个项目要使用哪个版本的 Gradle 来构建。
我们在 Android Studio 的 File -> Project Structure -> Project 里配置的 Gradle Version,最终改变的其实就是上述文件里最后一行的 Gradle 版本属性值:
第一行是gradle版本
第二行是android gradle插件版本
两者的对应关系见官方文档
官方说了,提供了 gradle/wrapper 这种方式,可以让你特别灵活的进行配置,想换个 Gradle 版本来构建项目,只需要修改这个配置文件的 Gradle 版本属性值即可,当然也可以直接通过 AS 提供的 UI 界面操作,结果都一样。
由于 Gradle 更新换代特别快,而且新的大版本经常都会提供很多新特性,这就导致了在 clone Github 上一些开源项目到本地构建时经常有报错的问题,本质原因就是因为它使用的 Gradle 版本跟你本地不一样,而由于有堵巨墙的原因,导致一直没法成功下载它配置的 Gradle 版本,进而就无法构建项目,而报错了。
网上说的一些解决方案是让你手动去修改 gradle-wrapper.properties 文件里的 Gradle 版本,改成你本地的版本,但我觉得这种方法不一定适用,这取决于那个项目中是否有用到一些新特性,以及你本地的 Gradle 版本是否兼容项目中用到的 Gradle 新特性。
通常来说,如果你本地的 Gradle 比克隆的项目的 Gradle 版本高的话,那么这种直接修改项目的 Gradle 版本方式应该是可行的,那么怎么知道你本地都有哪些 Gradle 版本呢:
就是上面gradle-wrapper.propertes里面配置的路径,在MAC下面,就是~/.gradle/wrapper/dists,只要你在 gradle-wrapper.properties 修改了 Gradle 的版本号,那么当你在构建项目时,就会先到你电脑的这个路径下查找相对应版本的 Gradle,如果可用,则直接进行构建项目任务,如果不存在,那么就会自动去下载对应版本的 Gradle。
gradle 有 3 种版本:
简而言之,就是一个配置全局的管理文件。
配置 gradle 运行环境的文件,比如配置 gradle 运行模式,运行时 jvm 虚拟机的大小。
举个栗子:
一般大型的项目都是拆分进行模块化开发的,这时候每个模块在合并的时候容易有冲突(假如版本冲突),或者需要改版本的时候比较麻烦,需要一个一个地方去改,这时候配置全局的 gradle.properties 文件就显得异常有帮助了。
例如:
# Project-wide Gradle settings.
#添加ndk支持(按需添加)
android.useDeprecatedNdk=true
# 应用版本名称
VERSION_NAME=1.0.0
# 应用版本号
VERSION_CODE=100
# 支持库版本
SUPPORT_LIBRARY=24.2.1
# MIN_SDK_VERSION
ANDROID_BUILD_MIN_SDK_VERSION=14
# TARGET_SDK_VERSION
ANDROID_BUILD_TARGET_SDK_VERSION=24
# BUILD_SDK_VERSION
ANDROID_BUILD_SDK_VERSION=24
# BUILD_TOOLS_VERSION
ANDROID_BUILD_TOOLS_VERSION=24.0.3
这样就为项目配置了全局属性,其他模块就可以直接使用了,需要改动的时候只需要在这个全局的配置文件中一键修改即可,如下:
android {
compileSdkVersion project.ANDROID_BUILD_SDK_VERSION as int
buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
applicationId project.APPLICATION_ID
versionCode project.VERSION_CODE as int
versionName project.VERSION_NAME
minSdkVersion project.ANDROID_BUILD_MIN_SDK_VERSION as int
targetSdkVersion project.ANDROID_BUILD_TARGET_SDK_VERSION as int
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
//这里注意是双引号
compile "com.android.support:appcompat-v7:${SUPPORT_LIBRARY}"
compile "com.android.support:design:${SUPPORT_LIBRARY}"
compile "com.android.support:recyclerview-v7:${SUPPORT_LIBRARY}"
compile "com.android.support:support-annotations:${SUPPORT_LIBRARY}"
compile "com.android.support:cardview-v7:${SUPPORT_LIBRARY}"
compile "com.android.support:support-v4:${SUPPORT_LIBRARY}"
}
这里可以看到有些属性后面有 as int ,这是因为原本的配置应该都是默认是字符串,然后用 as int 就可以轻松转换成 int 类型了。
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed May 30 15:32:23 CST 2018
ndk.dir=/Users/xxx/Library/Android/sdk/ndk-bundle
sdk.dir=/Users/xxx/Library/Android/sdk
Android Studio 自动创建的工程中,IDE 自动配备了本地配置文件,并且设置了 sdk.dir 属性。也就是配置了 Android_Home 环境变量,所以 IDE 能够自动生成这个配置文件。一般情况都不用修改这个配置文件,除非你一定要重新制定一个 SDK 路径。
gradlew 文件和 gradlew.bat 文件,两份没有什么差别,它们都是脚本文件,区别只是一个是 shell 脚本,一个是批处理脚本,那么自然一个是用来在 Linux 上运行,一个在 Windows 上运行。
它借助了 gradle/wrapper 目录下的 gradle-wrapper.jar 文件,并借助了 java 命令,提供了可让我们直接以命令行形式运行一些相应的 gradle 指令,而这些指令在 gradle-wrapper.jar 文件中都提供了相应的实现。
setting.gradle 文件通常是当项目涉及到多 Module 的场景。
只有在 setting.gradle 中 include 的 Module,才会被加入构建中,也就是说,如果一个项目文件夹内,包含了很多子工程,但如果没在 setting.gradle 中将这些子工程 include 进来的话, 这些 Module 是不会参与进构建的。
include ':app'
include ':library'
include ':other'
project(':other').projectDir = file('other/app')
如果子工程的这些 Module 都直接放在了项目根目录中,那么 setting.gradle 中只需要写 include 就可以了,那如果这些子工程是放在别的地方,那么也可以通过修改 project().projectDir 来指定子工程的具体路径,也就是说,所有的 Module 并不一定需要全部集中放在同一个项目内。
例如上面的other,它的结构可能是:
- MyApp
|-- app
|-- library
|-- other
|--- app
-- setting.gradle
每一个project都会有一个build文件,所以root project也会有一个。
一个项目中可能存在多个子工程,每个子工程构建都应该是相互独立的,也就是说,每个子工程都可以根据自己的需要,配置各种依赖,插件等。
那么,Gradle 是如何分开来管理每个子工程的构建任务的呢?
这就是 build.gradle 文件的作用了,所以你会发现,每个子工程,也就是每个 Module 都会有一个 build.gradle 文件,Gradle 就是以这个文件为根据来构建这个 Module。
那么,如果有些配置项,在所有的子工程中都是一致的话,如果在每个子工程里都去重复粘贴的话,当这个共同的配置项需要发生变化时,维护起来会非常麻烦,这也就是为什么根目录下面还会有一个 build.gradle 文件。
根目录下的这个 build.gradle 是统筹全局的,在这里,你可以配置一些所有工程共同的配置项,比如 Android Gradle 的版本,依赖库的仓库地址这些所有工程的共同配置项。
也就是说,其实将根目录下的 build.gradle 文件里的内容移到每一个工程下的 build.gradle 里,也是可行的。但没必要这样做,吃饱了撑着。
buildscript {
repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven {
url "file:///${rootProject.projectDir}/extras/m2repository/"
}
}
dependencies {
...
}
}
https://juejin.im/post/5c70a1f56fb9a04a0e2dd008
https://blog.csdn.net/shijianduan1/article/details/83410267
manifestPlaceholders = [hostName:"www.example.com",post:"8080"]
https://blog.csdn.net/u012149399/article/details/88124473
app
moduleA
moduleB
app如果有flavor不要求每一个module都有,但是如果有一个module有flavor,所有依赖这个moudle的app或者module都需要区分flavor
我们经常遇到com.android.support包冲突
All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes
大致意思就是com.android.support的包版本号要保持一致,但是可能我们自己新建的项目的com.android.support包版本号要高一些,一些第三方的库的com.android.support可能没有及时更新support库,就会出现这个错误。
所以说根据这个原因,解决方法就有三个:
既然要排除依赖,那么就需要知道,那些第三方依赖了com.android.support,以及其依赖版本号
gradlew -q app:dependencies
该命令会将app中所依赖的库展示出来:
所以排除这个库即可:
api ("com.alibaba:arouter-api:1.3.1") {
exclude group: 'com.android.support'
}
api ("com.alibaba:arouter-api:1.3.1") {
exclude group: 'com.android.support', module: 'support-v4'
}
一般来说,在我们自己创建library给别人使用时,如果需要依赖com.android.support的话,建议用compileOnly的方式依赖,这样只会在编译时有效,不会参与打包。
compileOnly 'com.android.support:appcompat-v7:26.1.0'
compileOnly 'com.android.support:design:26.1.0'
compileOnly 'com.android.support:support-vector-drawable:26.1.0'
在存在冲突的module中的build.gradle文件中加入下面代码:
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
if (requested.group == 'com.android.support') {
if (!requested.name.startsWith("multidex")) {
details.useVersion '28.0.0'
}
}
}
}
原理就是通过遍历所有依赖,并修改指定库的版本号
- requested.group == 'com.android.support'
com.android.support表示要修改的依赖库- details.useVersion '28.0.0'
28.0.0表示要修改的版本号
MyProject
- app
- build.gradle
- demolib
- xxx_debug.aar
- xxx_release.aar
- build.gradle
- demolib.iml
build.gradle(Module:demolib)
configurations.maybeCreate("default")
artifacts.add("default", file('xxx_release.aar'))
configurations.maybeCreate("debug")
artifacts.add("debug", file('xxx_debug.aar'))
build.gradle(Module:app)
dependencies {
implementation project(':demolib')
debugImplementation project(path: ':demolib', configuration: 'debug')
}
当我们需要引入aar的时候,可以像上面这样新建一个module,也可以采用这种方式
module-x
- libs
- xxx.aar
- src
build.gradle
我们把aar文件放到了libs下面,我们还需要修改build.gradle
android {
// ...
repositories {
flatDir { dirs 'libs' }
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api(name: 'onsdk_all', ext: 'aar')
}
如果是在library的module里面这样写,其他applicaiton module引入的时候会有问题:
module-library
- libs
- xxx.aar
- src
build.gradle
module-application
- src
build.gradle
并且module-application是依赖module-library的:
implementation project(':module-library')
这个时候,编译会报错,提示xxx.aar找不到,module-application也需要引入xxx.aar,如下:
module-library
- libs
- xxx.aar
- src
build.gradle
module-application
- libs
- xxx.aar
- src
build.gradle
然后module-application的build.gradle也需要修改:
android {
// ...
repositories {
flatDir { dirs 'libs' }
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api(name: 'onsdk_all', ext: 'aar')
}
记一些以前不知道的 Gradle 配置
我们经常遇到这样的问题:
com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK lib/armeabi/libgnustl_shared.so
File1: /Users/lipeng/.android/build-cache/1352369e8237b54bc36dca29ad3df6710a0e004b/output/jni
File2: /Users/lipeng/work/workspace/qcs.android.map/3dmap-navibaidu/build/intermediates/bundles/naviDebug/jni
特别是在使用第三方sdk的时候,那么如何解决呢?
android {
// ...
packagingOptions {
exclude 'META-INF/rxjava.properties'
pickFirst 'org/apache/http/entity/mime/version.properties'
pickFirst 'lib/armeabi/libgnustl_shared.so'
}
}
一般用法(assemble + Build Type) :
// 编译并打Debug包
./gradlew assembleDebug
// 编译并打Release的包
./gradlew assembleRelease
可能有多个可执行module(module + assemble):
// 名字为app的module,编译并打Debug包
./gradlew :app:assembleDebug
assemble + productFlavors :
// 给wandoujia渠道编译并打Debug包和Release包
./gradlew assembleWandoujia
assemble + productFlavors + Build Type:
// 打包wandoujia渠道的release版本
./gradlew assembleWandoujiaRelease
所以,根据上面的例子,我们得到的公式是:
./gradlew [:module][:]assemble[ProductFlavors][BuildType]
/Users/xxxx/.gradle/caches/modules-2/files-2.1
这个路径下面,就是gradle下载的所有aar了
假设我要打mylibrary包,那么:
点击右侧的assembleRelease,就会生成aar文件,位置在:
mylibrary/build/outputs/aar/mylibrary-release.aar
还有两个前提:
假设工程结构如下:
MyProject
- app
- build.gradle
- sdk
- build.gradle
- build.gradle
apply plugin: 'com.android.library'
// 这里!!!!!
apply plugin: 'maven'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
// 这里!!!!!
// 本地maven仓库地址
def localMavenRepo = 'file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath
uploadArchives {
repositories.mavenDeployer {
// 本地仓库路径
repository(url: localMavenRepo)
// 唯一标识
pom.groupId = "com.test.sdk"
// 项目名称
pom.artifactId = "sdk-name"
// 版本号
pom.version = "1.0.1"
}
}
主要需要注意上面注释的两处更改,像上面这样配置后,就会组装成这样:
implementation 'com.test.sdk:sdk-name:1.0.1'
上面的这个硬编码了本地地址,不太好,还有一种更好的写法:
uploadArchives {
repositories {
def localMavenRepo = mavenLocal().getUrl()
mavenDeployer {
// 本地仓库路径
repository(url: localMavenRepo)
// 唯一标识
pom.groupId = "com.test.sdk"
// 项目名称
pom.artifactId = "sdk-name"
// 版本号
pom.version = "1.0.1"
}
}
}
使用第三方插件
android-maven-gradle-plugin
apply plugin: 'com.github.dcendents.android-maven'
group = "com.test.sdk"
version = "1.0.1"
project.archivesBaseName = "sdk-name"
android.defaultPublishConfig "baiduRelease"
def hasAndroidPlugin() {
return getPlugins().inject(false) { a, b ->
def classStr = b.getClass().name
def isAndroid = ("com.android.build.gradle.LibraryPlugin" == classStr) || ("com.android.build.gradle.AppPlugin" == classStr)
a || isAndroid
}
}
task sourcesJar(type: Jar) {
if (hasAndroidPlugin()) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
} else {
from sourceSets.main.allSource
classifier = 'sources'
}
}
artifacts {
archives sourcesJar
}
def localMavenRepo = 'file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath
uploadArchives {
repositories {
def localMavenRepo = mavenLocal().getUrl()
mavenDeployer {
// 本地仓库路径
repository(url: localMavenRepo)
}
}
}
MyProject
- app
- build.gradle
- build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "config.gradle"
buildscript {
repositories {
jcenter()
// 这里!!!!!!
mavenLocal()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
}
}
allprojects {
repositories {
jcenter()
// 这里!!!!!!
mavenLocal()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
加上mavenLocal()
即可
implementation 'com.test.sdk:sdk-name:1.0.1'
注意:如果是多productFlavors,那么需要指定具体的
android.defaultPublishConfig "baiduRelease"
,这样才能发布成功
主流来说,是3位或者4位:
一般来说采用3位即可。
https://segmentfault.com/a/1190000021587501
https://blog.csdn.net/chengqiuming/article/details/103217889
对于 Maven 的自动处理传递性依赖版本冲突问题,是按最短路径和优先声明原则来处理.
而对于 Gradle 来说同样有着自动处理传递性依赖版本冲突问题的功能,只是 Gradle 是默认使用版本最高的.而针对一些特殊的需求还是需要使用手动解决.以下便是 Gradle 的手动处理版本冲突.