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.pswp.cn/news/160993.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160993.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160993.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

.Net6使用WebSocket與前端進行通信

1. 創建類WebSocketTest&#xff1a; using System.Net.WebSockets; using System.Text;namespace WebSocket.Demo {public class WebSocketTest{//當前請求實例System.Net.WebSockets.WebSocket socket null;public async Task DoWork(HttpContext ctx){socket await ctx.We…

為UE和Unity開發者準備的Godot指南

為UE和Unity開發者準備的Godot指南 ——兩位大哥打架&#xff0c;請帶上我 這兩天游戲行業又開始熱鬧了&#xff0c;昨天兩條信息直接刷爆朋友圈&#xff0c;最大的兩家游戲引擎公司懟起來了。 《為Unity開發者準備的虛幻引擎指南》&#xff1a; 為Unity開發者準備的虛幻引擎指…

sso 四種授權模式

單點登錄 單點登錄&#xff0c;英文是 Single Sign On&#xff08;縮寫為 SSO&#xff09;。即多個站點共用一臺認證授權服務器&#xff0c;用戶在站點登錄后&#xff0c;可以免登錄訪問其他所有站點。而且&#xff0c;各站點間可以通過該登錄狀態直接交互。例如&#xff1a; …

C#編程題分享(3)

n的階乘問題 輸?整數n&#xff0c;輸出n的階乘。 int n Convert.ToInt32(Console.ReadLine()); int jiecheng 1; for (int i 1; i < n 1; i) {jiecheng * i; // 1 * 2 * 3 * .....} Console.WriteLine("{0}的階乘是&#xff1a;{1}", n, jiecheng); q^n次…

Clickhouse設置多磁盤存儲策略

設置多磁盤存儲 clickhouse安裝完成以后&#xff0c;配置了一個默認的存儲空間&#xff0c; 這個只能配置一個目錄&#xff0c;如果要使用多個磁盤目錄&#xff0c;則需要配置磁盤組策略 查看當前的存儲策略 select name, path, formatReadableSize(free_space) as free, fo…

Django DRF版本號的處理

在restful規范中&#xff0c;后端的API中需要體現版本。如果項目比較大&#xff0c;需要些很多的視圖類&#xff0c;在每一個類中都寫一遍會比較麻煩&#xff0c;所以drf中也支持了全局配置。在每個版本處理的類中還定義了reverse方法&#xff0c;他是用來反向生成URL并攜帶相關…

還記得高中生物書上的莫斯密碼嗎?利用Python破解摩斯密碼的代碼示例!

文章目錄 前言摩爾斯電碼Python實現摩斯密碼對照表加密解密測試 完整代碼總結關于Python技術儲備一、Python所有方向的學習路線二、Python基礎學習視頻三、精品Python學習書籍四、Python工具包項目源碼合集①Python工具包②Python實戰案例③Python小游戲源碼五、面試資料六、Py…

Arduino驅動MLX90614紅外溫度傳感器(溫濕度傳感器)

目錄 1、傳感器特性 2、MLX90614發射率補償方法 3、控制器和傳感器連線圖 4、驅動程序 MLX90614紅外測溫模塊,通過探測物體紅外輻射能量的大小和波長的分布來檢測物體的表面溫度。紅外測溫器由光學系統、光電探測器、信號放大器

一文讀懂 Linux mmap

文章目錄 1.簡介2.實現原理3.相關函數4.mmap和常規文件操作的區別5.作用參考文獻 1.簡介 mmap&#xff08;memory map&#xff09;即內存映射&#xff0c;用于將一個文件或設備映射到進程的地址空間。 實現這樣的映射關系后&#xff0c;進程虛擬地址空間中一段內存地址將與文…

TorchScript C++ 自定義運算符 cpucuda

參考 在 C 中注冊調度運算符 使用自定義 C 運算符擴展 TorchScript 環境&#xff1a; NVIDIA Driver Version : 545.23.08CUDA Version: 12.1Python Version: 3.11Pytorch Version: 2.1Cmake version : 3.18.1工作目錄&#xff1a;workspace/test 一、 C 自定義運算符 創建…

逐字節講解 Redis 持久化(RDB 和 AOF)的文件格式

前言 相信各位對 Redis 的這兩種持久化機制都不陌生&#xff0c;簡單來說&#xff0c;RDB 就是對數據的全量備份&#xff0c;AOF 則是增量備份&#xff0c;而從 4.0 版本開始引入了混合方式&#xff0c;以 7.2.3 版本為例&#xff0c;會生成三類文件&#xff1a;RDB、AOF 和記…

2014年5月28日 Go生態洞察:GopherCon 2014大會回顧

&#x1f337;&#x1f341; 博主貓頭虎&#xff08;&#x1f405;&#x1f43e;&#xff09;帶您 Go to New World?&#x1f341; &#x1f984; 博客首頁——&#x1f405;&#x1f43e;貓頭虎的博客&#x1f390; &#x1f433; 《面試題大全專欄》 &#x1f995; 文章圖文…

Java面試附答案:掌握關鍵技能,突破面試難題!

問題&#xff1a;什么是大O表示法&#xff1f;它在Java中的應用是什么&#xff1f; 回答&#xff1a; 大O表示法是一種用來衡量算法復雜度的方法&#xff0c;它描述了算法的時間復雜度和空間復雜度的增長速度。它使用符號O(n)來表示算法的漸進時間復雜度&#xff0c;其中n表示…

如何讓Python2與Python3共存

安裝 首先分別安裝Py2和Py3&#xff0c;我都安裝到C盤根目錄里了&#xff0c;然后分別將Py2和Py3都配置到系統環境變量中去&#xff1a;C:\Python36\Scripts\;C:\Python36\;C:\Python27\;C:\Python27\Scripts; 配置 修改兩個版本的可執行文件名字 驗證 重新配置一下pip …

Ubuntu刪除應用圖標

刪除用戶下的圖標 sudo nautilus ~/.local/share/applications刪除系統下的圖標 sudo nautilus /usr/share/applications

大數據-之LibrA數據庫系統告警處理(ALM-25500 KrbServer服務不可用)

告警解釋 系統按30秒周期性檢測組件KrbServer的服務狀態。當檢測到組件KrbServer服務異常時產生該告警。 當檢測到組件KrbServer服務恢復時告警恢復。 告警屬性 告警ID 告警級別 可自動清除 25500 致命 是 告警參數 參數名稱 參數含義 ServiceName 產生告警的服務…

解決MySQL中某列數據過長無法入庫的問題-Details:data too long for column `xxx` at row 1

問題描述&#xff1a; 我在將軌跡的經緯度轉換為字符串入庫時&#xff0c;遇到寫入問題 Mysql數據入庫報錯&#xff1a; Caused by:java.long.exception:寫入數據庫表失敗.Details:data too long for column xxx at row 1&#xff0c;我的xxx字段類型是string,在mysql庫表中…

加速CI構建,實現高效流水線——CloudBees CI發布工作區緩存功能

加速軟件交付流程能夠更快接觸到客戶&#xff0c;獲得競爭優勢。然而&#xff0c;識別這一過程中存在的瓶頸可能頗具挑戰。讓我們從審查構建和測試階段開始著手。例如&#xff0c;當CI作業執行時間較長時&#xff0c;它會延遲開發人員的反饋循環&#xff0c;從而可能導致發布延…

使用Python解析CAN總線

緣起 在新能源車輛的開發和維護中&#xff0c;經常需要對CAN總線數據進行分析。CANOE等總線軟件雖然方便&#xff0c;但功能有限&#xff0c;難以滿足數據分析的要求。Matlab的Vehicle Network Toolbox可以方便的進行數據解析和分析&#xff0c;它是閉源且收費的。因此&#x…

SpringBoot啟動順序

前言 每次有人問起SpringBoot的啟動順序是不是又來翻博客了&#xff1f;其實只需要稍微查看Spring的源碼即可 步驟 SpringBoot的入口org.springframework.boot.SpringApplication#run(String... args), 這里面實現了SpringBoot程序啟動的所有步驟 啟動事件的順序可以看監聽器…