Gradle常用命令与参数依赖管理和版本决议

一、Gradle 常用命令与参数

本课程全程基于 Gradle8.0 环境

1、Gradle 命令

介绍 gradle 命令之前我们先来了解下 gradle 命令怎么在项目中执行。

1.1、gradlew

gradlew 即 Gradle Wrapper,在学习小组的第一课时已经介绍过了这里就不多赘述。提一下执行命令,一般网上都是 windows 下用 gradlew; Mac 或者 Linux 下用 ./gradlew 。实际上 AS Dolphin 2021.3.1 后的版本因为默认终端切换成 powershell 所以命令变成了 .\gradlew 或者 ./gradlew 都可以。我们接下来看看 gradlew 脚本的内容(Android Studio 项目根目录 gradlew 文件):

#!/usr/bin/env sh#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###############################################################################
##
##  Gradle start up script for UN*X
##
############################################################################### Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; dols=`ls -ld "$PRG"`link=`expr "$ls" : '.*-> \(.*\)$'`if expr "$link" : '/.*' > /dev/null; thenPRG="$link"elsePRG=`dirname "$PRG"`"/$link"fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/nullAPP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"warn () {echo "$*"
}die () {echoecho "$*"echoexit 1
}# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" inCYGWIN* )cygwin=true;;Darwin* )darwin=true;;MINGW* )msys=true;;NONSTOP* )nonstop=true;;
esacCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; thenif [ -x "$JAVA_HOME/jre/sh/java" ] ; then# IBM's JDK on AIX uses strange locations for the executablesJAVACMD="$JAVA_HOME/jre/sh/java"elseJAVACMD="$JAVA_HOME/bin/java"fiif [ ! -x "$JAVACMD" ] ; thendie "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOMEPlease set the JAVA_HOME variable in your environment to match the
location of your Java installation."fi
elseJAVACMD="java"which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; thenMAX_FD_LIMIT=`ulimit -H -n`if [ $? -eq 0 ] ; thenif [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; thenMAX_FD="$MAX_FD_LIMIT"fiulimit -n $MAX_FDif [ $? -ne 0 ] ; thenwarn "Could not set maximum file descriptor limit: $MAX_FD"fielsewarn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"fi
fi# For Darwin, add options to specify how the application appears in the dock
if $darwin; thenGRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; thenAPP_HOME=`cygpath --path --mixed "$APP_HOME"`CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`JAVACMD=`cygpath --unix "$JAVACMD"`# We build the pattern for arguments to be converted via cygpathROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`SEP=""for dir in $ROOTDIRSRAW ; doROOTDIRS="$ROOTDIRS$SEP$dir"SEP="|"doneOURCYGPATTERN="(^($ROOTDIRS))"# Add a user-defined pattern to the cygpath argumentsif [ "$GRADLE_CYGPATTERN" != "" ] ; thenOURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"fi# Now convert the arguments - kludge to limit ourselves to /bin/shi=0for arg in "$@" ; doCHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an optionif [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a conditioneval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`elseeval `echo args$i`="\"$arg\""fii=`expr $i + 1`donecase $i in0) set -- ;;1) set -- "$args0" ;;2) set -- "$args0" "$args1" ;;3) set -- "$args0" "$args1" "$args2" ;;4) set -- "$args0" "$args1" "$args2" "$args3" ;;5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;esac
fi# Escape application args
save () {for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; doneecho " "
}
APP_ARGS=`save "$@"`# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"exec "$JAVACMD" "$@"

主要干了这几件事

  1. 设置 APP_HOME
  2. 配置默认的 JVM 选项
  3. 设置 CLASSPATH 路径,即指定 gradlew-wrapper.jar 路径
  4. 检查 JAVA 环境配置等

1.2、命令大全

执行 ./gradlew --help gradle 会为我们输出帮助信息:(这里只列出了部分,更多可以自己敲命令去看看)

PS D:\workspace\GradleDemo> ./gradlew --helpTo see help contextual to the project, use gradlew helpUSAGE: gradlew [option...] [task...]-?, -h, --help                     Shows this help message.
-b, --build-file                   Specify the build file. [deprecated]
--build-cache                      Enables the Gradle build cache. Gradle will try to reuse outputs from previous builds.
-c, --settings-file                Specify the settings file. [deprecated]
--configuration-cache              Enables the configuration cache. Gradle will try to reuse the build configuration from previous builds. [incubating]
--configuration-cache-problems     Configures how the configuration cache handles problems (fail or warn). Defaults to fail. [incubating]
--configure-on-demand              Configure necessary projects only. Gradle will attempt to reduce configuration time for large multi-project builds. [incubating]
--console                          Specifies which type of console output to generate. Values are 'plain', 'auto' (default), 'rich' or 'verbose'.
--continue                         Continue task execution after a task failure.
-D, --system-prop                  Set system property of the JVM (e.g. -Dmyprop=myvalue).
-d, --debug                        Log in debug mode (includes normal stacktrace).
--daemon                           Uses the Gradle daemon to run the build. Starts the daemon if not running.
--export-keys                      Exports the public keys used for dependency verification.
-F, --dependency-verification      Configures the dependency verification mode. Values are 'strict', 'lenient' or 'off'.
--foreground                       Starts the Gradle daemon in the foreground.
-g, --gradle-user-home             Specifies the Gradle user home directory. Defaults to ~/.gradle
-I, --init-script                  Specify an initialization script.
-i, --info                         Set log level to info.
-p, --project-dir     

这些命令也是可以简写的,例如 --help 可以简写为 -?-h

1.3、命令格式

gradle 命令的整体格式如下:

gradle [taskName...] [--option-name...]

2 gradle 常见命令

根据命令的功能目的,Gradle 命令大致可以分为下图的这些类型。

2.1 gradle 自身相关

2.1.1 查看 gradle 版本

查看 gradle 版本一般我们都是直接看项目中的 gradle.property 文件,里面的 distributionUrl 参数。除了这种方式,我们还可以通过gradle 命令查看: 输入:

./gradlew -version
//or
./gradlew -v

输出:

------------------------------------------------------------
Gradle 8.0
------------------------------------------------------------Build time:   2023-02-13 13:15:21 UTC
Revision:     62ab9b7c7f884426cf79fbedcf07658b2dbe9e97 Kotlin:       1.8.10
Groovy:       3.0.13
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.8 (Amazon.com Inc. 17.0.8+7-LTS)
OS:           Mac OS X 13.0.1 x86_64
2.1.2 gradle 版本升级

1、直接修改工程 gradle/wrapper/gradle-wrapper.properties 文件的 distributionUrl修改 gradle 版本 2、File>Project Structure 修改 AndroidGradle Plugin Version 和 Gradle 的版本然后 apply 3、使用命令行输入命令升级

./gradlew wrapper --gradle-version xxx
2.1.3 列出某个命令将要执行的所有任务(并不是真的执行它)
./gradlew xxx --dry-run

其中xxx就是要执行的命令,我们在学习的时候可以通过这个命令来快速的查看某个命令将要执行哪些任务。也方便快速对比相似功能的命令在执行过程中有哪些差异。

2.2 编译命令

2.2.1 编译打包
./gradlew build
//or
./gradlew assemble

这两个命令都是编译并输出全部类型的包。他们之间差别在于,build 命令是包含了 assemble 命令的全生命周期的,从上面的 task --all 命令说明也可以看出。下面的这个图可以更直观的反映:

分别执行 ./gradlew assemble --dry-run./gradlew assemble --dry-run命令,把输出结果进行对比。差别就是 build 命令会在 assemble 之后执行额外的 check 任务

2.2.2 编译 debug 包
./gradlew assembleDebug
//or
//反映
./gradlew installDebug

两个任务前面步骤一致,最后一步不一样,分别是执行 :app:assebmleDebug:app:installDebug

2.2.3 编译 release 包
./gradlew assembleRelease
//or
./gradlew installRelease

两个任务前面步骤一致,最后一步不一样,分别是执行 :app:assebmleRelease:app:installRelease

2.2.4 编译并打印日志
./gradlew assembleDebug --info
//or
./gradlew assembleRelease --info

加上 --info,编译日志会更完整,方便定位问题。

2.3 清除命令

./gradlew clean

等同于 Build > Clean Project,会清除构建目录下的产物

2.4 卸载命令

./gradlew uninstallDebug
//or
./gradlew uninstallRelease

这个命令是直接卸载连接设备上的当前项目安装包,跟 adb 指定包名进行卸载一样

2.5 调试命令

当我们使用 gradle 进行打包报错时,调试命令就可以帮我们更方便的定位出问题原因

2.5.1 编译并输出堆栈日志
./gradlew assembleDebug --stacktrace
//or
./gradlew assembleDebug -s

或者可以在参数前加上 --full 输出更详细的日志信息

./gradlew assembleDebug --full-stacktrace
//or
./gradlew assembleDebug --full-s
2.5.2 设置日志级别

Gradle 的日志分级

Level作用
ERROR错误
WARNING警告
LIFECYCLE生命周期
INFO信息
DEBUG调试
TRACE跟踪

日志过滤

过滤命令作用
-q 或 -quiet只显示 ERROR 和 WARNING 日志
–i 或 --info显示 INFO 及以上日志
-d 或 --debug显示 DEBUG 及以上日志

示例:

./gradlew assembleDebug -e

2.6 任务相关命令

2.6.1 查看项目 gradle task
./gradlew tasks //只列出关键的 task
//or
./gradlew tasks --all //所有的 task 都将列出

这个命令将列出项目所有要执行的 task,并且会有这个 task 的相应说明信息(这里只截取了一部分)

2.6.2 执行 Task
./gradlew <taskName>
//or
./gradlew :<moduleName>:<taskName>

可以在 AndroidStudio 右边栏的 Gradle 菜单下选择 Tasks 执行也是一样的效果

2.7 依赖查看命令

依赖查看命令可以让我们更清晰的了解引入的依赖库,解决因为依赖导致的错误或者剔除一些不需要的依赖

./gradlew app:dependencies //输出 app 模块依赖树
//or
./gradlew dependencies > xxx.txt //输出依赖树到 xxx.txt 文件

依赖关系树后面有的会跟(*)``(c)有的又没有,代表如下意义:

  1. * :表示该依赖项已被标记为被选中的版本。这意味着它是根据解析规则中的约束条件被选择的版本,而不是默认的最新版本。这通常是由于在项目的构建配置中明确指定了依赖项的版本或者存在其他依赖项对其版本有限制的情况。
  2. c :表示该依赖项是由于冲突解决而被强制选择的版本。当项目中存在多个依赖项,它们对同一依赖的版本有不同的要求时,Gradle 将尝试解决这些冲突。如果无法找到满足所有依赖项的版本,Gradle 将选择一个版本并标记为冲突(conflict)。(c) 标记表示该依赖项是由于冲突解决而被强制选择的版本。

举个例子:

假设项目 A 依赖于库 X 的版本 1.0,而项目 B 依赖于库 X 的版本 2.0。这两个项目都作为依赖项添加到同一个项目 C 中。现在,项目 C 需要同时满足项目 A 和项目 B 的依赖。

Gradle 将尝试解决这个冲突,它会检查是否有一个版本可以同时满足项目 A 和项目 B 的依赖。如果存在满足所有依赖项的版本,Gradle 将选择该版本,并将其标记为被选中的版本 (*)

然而,如果无法找到满足所有依赖项的版本,Gradle 将不得不做出一个决策。在这种情况下,Gradle 会选择一个版本,并将其标记为冲突 (c)。这表示所选的版本是为了解决冲突而被强制选择的,可能无法完全满足所有依赖项的要求。

2.8 性能相关命令

2.8.1 使用本地缓存版本离线编译
./gradlew assembleDebug --offline

也可以在AndroidStudio 侧边栏可视化开启关闭。差别在于,如果没开启离线编译,每次编译的时候都会去检查版本,对于没指定版本号或者动态版本号的依赖每次都会以最新的版本进行编译。

2.8.2 构建缓存
./gradlew assembleDebug --build-cache //开启
./gradlew assembleDebug --no-build-cache //不开启
2.8.3 配置缓存
./gradlew assembleDebug --configuration-cache //开启
./gradlew assembleDebug --no-configuration-cache //不开启
2.8.4 并行构建
./gradlew assembleDebug --parallel //开启
./gradlew assembleDebug --no-parallel //不开启

性能相关的这几个命令执行效果,和直接在 gradle.properties 中配置效果是一样的。

2.8.5 编译性能报告
./gradlew assembleDebug --profile

会在项目目录的 build > reports > profile 路径下生成报告文件

./gradlew assembleDebug --scan

会生成更详细的在线报告,首次执行会邮箱验证授权,完成后会输出一个链接。

2.9 动态传参命令

./gradlew assembleDebug -PisTest=true

build.gradle 中通过如下方式获取参数值

if(project.hasProperty("isTest")){
// do something
}else{
// do something else
}

通过 hasProperty 来获取命令行的参数

project.getProperty("isTest")

上面的方式都是判断参数是否存在,那么怎么获取参数的值呢?

if(project.hasProperty("isTest")){println("******hasProperty isTest yes******")if(Boolean.valueOf(project.getProperty('isTest')){println("******property isTest true******")}else{println("******property isTest false******")}
}else{println("******hasProperty isTest no******")
}

获取参数的数据类型需要基本数据类型转换成对应的类型,即 XXX.valueOf(getProperty('key'))。通过动态参数,我们可以在打包过程中做一些差异化的构建。

二、依赖管理和版本决议

在我们的开发中,经常会遇到各个模块中引入了相同的三方库,但是三方库版本又各不相同。有时候只是升级了一下某个依赖库存,结果运行就各种报错了。这部分课程将介绍一下 Gradle 的依赖管理方式和版本决议机制,弄明白 Gradle 是怎么把三方库引入到项目中的。在遇到同库不同版本的时候又是怎样决定使用哪个版本的。

1、依赖管理

不使用 maven 方式接入三方库,我们一般都是直接导入 jar 包或者 aar 包。替换版本的时候又重新去导入新的版本的 jar 或者 aar。遇到导入了相同 jar 或者 aar 的时候还出现冲突编不过。如果我们基于 Gradle 来开发项目,则可以依靠 Gradle 比较灵活方便的对依赖库版本进行管理。下图是 Gradle 工作的大致示意图:

构建过程中,Gradle 会先从本地检索,找不到就挨个从远端找依赖库,然后缓存到本地。 我们也可以通过以下方式,开启离线编译,对于未指定版本号或者使用动态版本号的依赖使用本地的版本进行编译

1.1 声明依赖

1.1.1 配置 maven 和依赖信息

声明 maven 依赖包含两部分:

  • 仓库配置 我这里是在 gradle 8.0 的环境下演示的。配置信息在 projetc > settings.gradle 下,低版本配置信息一般在 project > build.gradle 下
pluginManagement {repositories {//指定 maven 仓库的地址maven { url "https://www.jitpack.io" }google()//maven 官方仓库地址mavenCentral()//自定义 gitlab 仓库作为 maven 地址maven {url "https://gitee.com/monkeies/maven/raw/master"}//本地 maven 仓库地址maven {url uri("/Users/pandaq/WorkSpace/AndroidProjects/Aizhong/Maven/")}//http 地址需要配置 allowInsecureProtocol = truemaven { url 'http://xxx.xxx.xxx/xxx/'allowInsecureProtocol = true}}
}
  • 依赖库配置 依赖库的配置信息在各个 module 的 build.gradle 下
implementation 'com.airbnb.android:lottie:6.1.0'
1.1.2 依赖类型
  • 本地模块依赖
  • 本地二进制文件依赖
  • 远端二进制文件依赖
dependencies { 
// Dependency on a local library module 
implementation project(':mylibrary') 
// Dependency on local binaries 
implementation fileTree(dir: 'libs', include: ['*.jar']) 
// Dependency on a remote binary 
implementation 'com.example.android:app-magic:12.3' }

1.2 远端仓库

1.1.1 中我们配置的 repositories ,里面的 url 就是依赖项上川岛远端仓库的 url,远端仓库起一个桥梁的作用,把开发者和依赖库作者连接起来。开发者、远端仓库、依赖库开发者三者关系大概如下图所示:

  1. 左侧是我们项目的开发过程,通过声明依赖库信息和配置远端仓库地址,从而找到我们想要的 Lib;
  2. 中间为远端仓库,包含了丰富的 library/组件/插件;
  3. 右侧是 Lib 组件的开发者,把代码通过 aar/jar的形式打包到远端仓库提供给使用方;

1.3 GAV

GAV 即 groupId、artifactId、version , maven 根据这三个维度的规则确定 Library 的唯一性。我们日常的依赖引入是这样的:

implementation 'com.airbnb.android:lottie:6.1.0'

完整的内容其实是这样的:

implementation group: 'com.airbnb.android', name: 'lottie', version: '6.1.0'

打开 maven 搜索对应的 Library,我们可以看到更多依赖库的相关信息:

在 Maven 依赖方式下可以更清晰的看出一个依赖库引用方式的结构:

  • groupId:依赖库发布组织的名称,一般是公司域名倒写.包名;
  • artifactId:项目名称,如果 groupId 包含了项目名称,这里就是子项目名称;
  • version:版本号,这个库的具体版本号;

这里我们已系统设置的 DataReporter 这个 Lib 为例,发布端配置信息如下

1.4 依赖传递

Gradle 除了帮我们下载依赖,还提供了依赖传递的能力。根据我们的依赖引入方法不同,有着不同的依赖传递效果。

依赖方法说明示例
implementation编译时对模块可用,运行时对模块的消费者可用A 依赖 B,B 依赖 C;在编译和运行时 B均可用 C的代码;A 不能编译时用 C 代码,但运行时可访问 C 代码。
api编译和运行时对模块和模块的消费者都可用A 依赖 B,B 依赖 C;编译和运行时 A、B 都可以用 C 的代码
compileOnly仅编译时对模块可用,编译和运行时对模块消费者不可用,运行时对所有都不可用A 依赖 B,B 依赖 C;仅编译时 B 可以访问 C,运行时是没有 C 的任何代码的
runtimeOnly仅在运行时对模块和其消费者可用A 依赖 B,B 依赖 C;A、B都无法调用 C 的代码,但是 C 的代码会被打包到 apk 中,一般用于组件化隔离代码。

2、版本决议

既然各个模块都能引入依赖,并且依赖还具有传递性,那么必然存在某两个或几个模块都引入了某个依赖库。当他们引入版本不一样的时候问题就来了,最终构建项目时以哪个版本的依赖库为准呢?

2.1 依赖信息

前面课程已经讲过通过./gradlew app:dependencies这个命令,我们可以输出项目的依赖树。通过 build --scan,或者AndroidStudio 的 Gradle 侧边栏 Gradle > app > help > dependencies 也是一样的效果。

输出的依赖树包含了模块所有的直接依赖和间接依赖信息,从图中我们可以看到,okhttp 是有4.9.34.10.0两个版本的。图的 4.9.3 -> 4.10.0 表示 lib 模块中的版本被拔高到了 4.10.0,因为 app 中引入了更高的 4.10.0 版本。接下来我们一起来了解一下,gradle 是以怎样的规则来决定用哪个版本的。

2.2 决议规则

我们直接通过不同的示例来了解 gradle 的版本决议规则:

示例1:同模块不同版本

implementation 'com.squareup.okhttp3:okhttp:4.10.0'  
implementation 'com.squareup.okhttp3:okhttp:4.9.3'

结论:同模块引入不同版本,以更高版本为准

示例2:同模块使用强制关键字

//app
implementation 'com.squareup.okhttp3:okhttp:4.10.0'  
implementation ('com.squareup.okhttp3:okhttp:4.9.3'){version{structly("4.9.3")//strictly("4.10.0")}
}

结论:低版本中如果用 strictly 强制低版本会报错,强制高版本会给高版本标识 strictly

示例3:不同同模块不同版本

1、app 中引入 4.10.0,lib 中引入 4.9.3

//app
implementation 'com.squareup.okhttp3:okhttp:4.10.0'  

//lib
implementation 'com.squareup.okhttp3:okhttp:4.9.3'

2、app 中引入 4.9.3,lib 中引入 4.10.0

//app
implementation 'com.squareup.okhttp3:okhttp:4.9.3'

//lib
implementation 'com.squareup.okhttp3:okhttp:4.10.0'  

结论:不同模块引入不同版本,以版本高的为准

示例4:子模块使用强制关键字

1、app 引入4.10.0,lib 中强制 4.9.3

//app
implementation 'com.squareup.okhttp3:okhttp:4.10.0'

//lib
implementation ('com.squareup.okhttp3:okhttp:4.9.3'){version{strictly("4.9.3")}
}

2、app 强制4.9.3,lib 中引入4.10.0

//app
implementation ('com.squareup.okhttp3:okhttp:4.9.3'){version{strictly("4.9.3")}
}

//lib
implementation 'com.squareup.okhttp3:okhttp:4.10.0'

结论:子模块使用 strictly 强制低版本会报错,主模块使用 strictly 可以实现子模块降版本,可能导致子模块兼容问题。

总结

  1. 默认规则总是取高版本覆盖低版本
  2. strictly 关键字主工程可以实现降低编译版本
  3. 同模块或者子模块使用 strictly 降版本会直接报错

2.3 版本号规则

gradle 版本决议时的版本号规则如下表所示:

分类示例决议结果说明
全数字,段数不同1.2.3 vs 1.31.3段数从左到右比较,高的胜
全数字,段数相同1.2.3 vs 1.2.41.2.4段数从左到右比较,高的胜
全数字,段数相同,位数不同1.2.10 vs 1.2.31.2.10段数从左到右比较,高的胜
全数字,段数不同1.2.3 vs 1.2.3.01.2.3.0段数多的胜
段数相同,字母比较1.2.a vs 1.2.b1.2.b字母大胜
段数相同,数字与非数字1.2.3 vs 1.2.abc1.2.3数字优先于字母

另外 gradle 也支持版本号范围选项,如[1.0,)[1.1,2.0)latest.release

2.4 冲突解决

当项目比较复杂,依赖层级和module较多的时候,很多依赖传递就变得不可控了,时常会遇到各种版本依赖冲突。不管采用哪种方式,我们都需要有一个决议机制,保证依赖版本全局的唯一性,大家遵循这个机制进行依赖管理才能尽可能的避免这个问题。

2.4.1 版本管理(提前预防)

在项目实施过程中我们一般有以下方式对依赖版本进行约束:

  1. 配置统一的 gradle.config 放在云端统一进行维护,所有工程要增加三方依赖都通过这个文件进行配置,这样可以做到编译过程各个module三方依赖的版本统一。像我们现在项目中统一配置的 config.gradle。目前看应该是项目级别进行了配置,其实也可以迁移到云端统一管理,多项目共用
  2. 模块配置一个公共的 module_build.gradle,里面可以把必须项比如 kotlin 、androidx 这些依赖放在里面,使用模块再 apply from 的形式引入进去。
2.4.2 强制版本(临时补救)

项目实施中途遇到冲突了,再去统一版本显然不现实,那么我们有什么小范围的临时补救措施呢:

1、exclude 单独去掉某个依赖的冲突

//去除com.github.niorgai:StatusBarCompat:2.1.3引用的com.android.support包下内容 implementation('com.github.niorgai:StatusBarCompat:2.1.3') {exclude group: 'com.android.support' 
}) 
//去除module引用的'com.google.code.findbugs:jar305'的相关内容 
implementation(project(':downlibrary')) { exclude group: 'com.google.code.findbugs', module: 'jsr305' 
}

2、去掉所有 module 中的指定依赖

configurations { implementation.exclude group: 'com.example', module: 'lib' 
}

lib 中的依赖变成了 app 中的 4.10.0 版本,并且没有以来冲突标识

3、强制所有module使用指定版本

//app build.gradle 中配置
configurations.configureEach {  resolutionStrategy {  force 'com.squareup.okhttp3:okhttp:4.11.0'  }  
}

app 中版本由 4.10.0 升到了 4.11.0;lib1 版本由 4.9.0 升级到了 4.11.0;lib 版本由 4.10.0 升级到了4.11.0

2.4.3 面向 Bug 编程

开启版本冲突强检测模式,此模式下编译只要有版本冲突就会报错,再反过去顺藤摸瓜解决版本冲突。

configurations.configureEach {  resolutionStrategy { failOnVersionConflict() }  
}

Android 学习笔录

Gradle 篇:https://qr18.cn/DzrmMB
Android 性能优化篇:https://qr18.cn/FVlo89
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Kotlin 篇:https://qr18.cn/CdjtAF
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/201663.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux学习第44天:Linux 多点电容触摸屏实验(二):难忘记第一次牵你手的温存

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图内容如下&#xff1a; 二、硬件原理图分析 三、实验程序编写 1、修改设备树 1&#xff09;、添加FT5426所使用的IO 一个复位 IO、一个中断 IO、…

合并区间问题

以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;intervals [[1,…

React函数组件渲染两次

渲染两次是因为react默认开启了严格模式 React.StrictMode标签作用&#xff1a; 1、识别不安全的生命周期 2、关于使用过时字符串 ref API 的警告 3、关于使用废弃的 findDOMNode 方法的警告 4、检测意外的副作用 5、检测过时的 context API 注释掉React.StrictMode即为关闭严…

Kotlin学习——hello kotlin 函数function 变量 类 + 泛型 + 继承

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

01背包与完全背包学习总结

背包问题分类见下图 参考学习点击&#xff1a;代码随想录01背包讲解 01背包问题&#xff1a; 核心思路&#xff1a; 1、先遍历物品个数&#xff0c;再遍历背包容量。因为容量最先是最大的&#xff0c;往背包里放物品&#xff0c;所以背包容量在慢慢减少&#xff0c;但背包容量…

vue3+elementPlus登录向后端服务器发起数据请求Ajax

后端的url登录接口 先修改main.js文件 // 导入Ajax 前后端数据传输 import axios from "axios"; const app createApp(App) //vue3.0使用app.config.globalProperties.$http app.config.globalProperties.$http axios app.mount(#app); login.vue 页面显示部分…

UE5 操作WebSocket

插件&#xff1a;https://www.unrealengine.com/marketplace/zh-CN/product/websocket-client 参考&#xff1a;http://dascad.net/html/websocket/bp_index.html 1. 安装Plugings 2.测试websocket服务器 http://www.websocket-test.com/ 3.连接服务器 如果在Level BP里使用&a…

VMware 16 Pro 安装以及下载

1、下载地址&#xff1a; https://www.aliyundrive.com/s/nj3PSD4TN9G 2、安装文件 右击打开 下一步 密钥&#xff1a;ZF3R0-FHED2-M80TY-8QYGC-NPKYF 到此&#xff0c;安装完毕

YOLOv8 加持 MobileNetv3,目标检测新篇章

🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv8:我的专业笔记与技术总结   -- YOLOv8轻松上手, 适用技术小白,文章代码齐全,仅需 …

【Node.js】大前端技能最通俗易懂的讲解 快速入门必看

目录 1、概述前端工具VSCode安装 2、NodeJS的安装 3、NodeJS了解和快速入门 4、NodeJS实现HttpServer服务 5、NodeJS实现操作MySQL数据库 Node.js是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;它允许开发者在服务器端执行Node.js是一个基于Chrome V8引擎的Ja…

【西行纪年番】孙悟空对战阴界王,素衣奄奄一息,巨灵拳霸气一击

Hello,小伙伴们&#xff0c;我是拾荒君。 《西行纪年番》第20集已更新。为了救回素衣&#xff0c;孙悟空想尽办法&#xff0c;最后他拜托沙悟净帮忙&#xff0c;终于成功把自己传送到阴界。原来&#xff0c;素衣的魂魄被阴界王藏在了他制造的人偶之中。沙悟净提醒孙悟空必须在…

【Computer Vision Foundation】全球计算机视觉基金会论文网

计算机视觉基金会&#xff08;Computer Vision Foundation&#xff0c;简称CVF&#xff09;是一个致力于推动计算机视觉领域研究和发展的组织。以下是关于计算机视觉基金会的一些基本信息&#xff1a; 成立目的&#xff1a; CVF成立的目的是促进计算机视觉领域的学术研究、技术…

【追求卓越09】算法--散列表(哈希表)

引导 通过前面几个章节的学习&#xff08;二分查找&#xff0c;跳表&#xff09;&#xff0c;我们发现想要快速查找某一个元素&#xff0c;首先需要将所有元素进行排序&#xff0c;再利用二分法思想进行查找&#xff0c;复杂度是O(logn)。有没有更快的查找方式呢&#xff1f; 本…

2023年【上海市安全员C证】考试及上海市安全员C证找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年上海市安全员C证考试为正在备考上海市安全员C证操作证的学员准备的理论考试专题&#xff0c;每个月更新的上海市安全员C证找解析祝您顺利通过上海市安全员C证考试。 1、【多选题】2017年9月颁发的《中共上海市委…

el-tree 与table表格联动

html部分 <div class"org-left"><el-input v-model"filterText" placeholder"" size"default" /><el-tree ref"treeRef" class"filter-tree" :data"treeData" :props"defaultProp…

基于区域划分的GaN HEMT 准物理大信号模型

GaN HEMT器件的大信号等效电路模型分为经验基模型和物理基模型。经验基模型具有较高精度但参数提取困难&#xff0c;特别在GaN HEMT器件工艺不稳定的情况下不易应用。相比之下&#xff0c;物理基模型从器件工作机理出发&#xff0c;参数提取相对方便&#xff0c;且更容易更新和…

【DevOps】Git 图文详解(五):远程仓库

Git 图文详解&#xff08;五&#xff09;&#xff1a;远程仓库 1.远程用户登录1.1 &#x1f511; 远程用户登录&#xff1a;HTTS1.2 &#x1f511; 远程用户登录&#xff1a;SSH 2.远程仓库指令 &#x1f525;3.推送 push / 拉取 pull4.fetch 与 pull 有什么不同 &#xff1f; …

【C++】vector的介绍与使用

&#x1f9d1;‍&#x1f393;个人主页&#xff1a;简 料 &#x1f3c6;所属专栏&#xff1a;C &#x1f3c6;个人社区&#xff1a;越努力越幸运社区 &#x1f3c6;简 介&#xff1a;简料简料&#xff0c;简单有料~在校大学生一枚&#xff0c;专注C/C/GO的干货分…

数据库实验二 数据库表的数据插入、修改、删除操作

数据库实验二 数据库表的数据插入、修改、删除操作 一、实验目的二、设计性实验三、观察与思考 一、实验目的 1&#xff0e;掌握MySQL数据库表的数据插入、修改、删除操作SQL语法格式 2&#xff0e;掌握数据表的数据的录入、增加和删除的方法 二、设计性实验 某超市的食品管…

报错注入 [极客大挑战 2019]HardSQL1

打开题目 输入1或者1"&#xff0c;页面均回显NO,Wrong username password&#xff01;&#xff01;&#xff01; 那我们输入1 试试万能密码 1 or 11 # 输入1 and 12 # 输入1 union select 1,2,3 # 输入1 ununionion seselectlect 1,2,3 # 输入1 # 输入1# 页面依旧回…