Android Gradle使用詳解(二)--添加依賴

Android Gradle使用技巧-gradle使用詳細介紹
Android Gradle使用詳解(一)–設置applicationId

利用 Android Studio 中的 Gradle 構建系統,您可以輕鬆地將外部二進制文件或其他庫模塊作爲依賴項添加到您的 build 中。這些依賴項可位於您的計算機上或遠程代碼庫中,並且它們聲明的所有傳遞依賴項也會自動包含在內。

注意:指定依賴項時,不應使用動態版本號,如 ‘com.android.tools.build:gradle:3.+’。使用此功能可能會導致意外的版本更新和難以解析版本差異。

一、依賴項類型

如需向您的項目添加依賴項,請在 build.gradle 文件的 dependencies 代碼塊中指定依賴項配置,如 implementation。
例如,應用模塊的以下 build.gradle 文件包含三種不同類型的依賴項:

apply plugin: 'com.android.application'

android { ... }

dependencies {
    // 對本地庫模塊的依賴
    implementation project(":mylibrary")
    //或者
    implementation project(path: ":mylibrary")

    // 對本地二進制文件的依賴
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    
    // 對遠程二進制文件的依賴
    implementation 'com.example.android:app-magic:12.3'
}

其中每種依賴項配置都請求不同種類的庫依賴項,如下所示:

1、本地庫模塊依賴項

    implementation project(':mylibrary')

這聲明瞭對一個名爲“mylibrary”(此名稱必須與在您的 settings.gradle 文件中使用 include: 定義的庫名稱相符)的 Android 庫模塊的依賴關係。在構建您的應用時,構建系統會編譯該庫模塊,並將生成的編譯內容打包到 APK 中。

2、本地二進制文件依賴項

    implementation fileTree(dir: 'libs', include: ['*.jar'])

Gradle 聲明瞭對項目的 module_name/libs/ 目錄中 JAR 文件的依賴關係(因爲 Gradle 會讀取 build.gradle 文件的相對路徑)。
或者,您也可以按如下方式指定各個文件:

   implementation files('libs/foo.jar', 'libs/bar.jar')

3、遠程二進制文件依賴項

    implementation 'com.example.android:app-magic:12.3'

這實際上是以下代碼的簡寫形式:

    implementation group: 'com.example.android', name: 'app-magic',     version: '12.3'

這聲明瞭對“com.example.android”命名空間組內的 12.3 版“app-magic”庫的依賴關係。

注意:此類遠程依賴項要求您聲明適當的遠程代碼庫,Gradle 應在其中查找相應的庫。如果本地不存在相應的庫,則當 build 需要它時(例如,當您點擊 Sync Project with Gradle Files 圖標或運行 build 時),Gradle 會從遠程站點提取它。

二、依賴項配置

在 dependencies 代碼塊內,您可以從多種不同的依賴項配置中選擇其一(如上面所示的 implementation)來聲明庫依賴項。每種依賴項配置都向 Gradle 提供了有關如何使用該依賴項的不同說明。下表介紹了您可以對 Android 項目中的依賴項使用的各種配置。此表還將這些配置與自 Android Gradle 插件 3.0.0 起棄用的配置進行了比較

新配置 已棄用配置 行爲
implementation compile Gradle 會將依賴項添加到編譯類路徑,並將依賴項打包到構建輸出。不過,當您的模塊配置 implementation 依賴項時,會讓 Gradle 瞭解您不希望該模塊在編譯時將該依賴項泄露給其他模塊。也就是說,其他模塊只有在運行時才能使用該依賴項。 注意:使用此依賴項配置代替 api 或 compile(已棄用)可以顯著縮短構建時間,因爲這樣可以減少構建系統需要重新編譯的模塊數。例如,如果 implementation 依賴項更改了其 API,Gradle 只會重新編譯該依賴項以及直接依賴於它的模塊。大多數應用和測試模塊都應使用此配置。
api compile Gradle 會將依賴項添加到編譯類路徑和構建輸出。當一個模塊包含 api 依賴項時,會讓 Gradle 瞭解該模塊要以傳遞方式將該依賴項導出到其他模塊,以便這些模塊在運行時和編譯時都可以使用該依賴項。 注意:此配置的行爲類似於 compile(現已棄用),但使用它時應格外小心,只能對您需要以傳遞方式導出到其他上游消費者的依賴項使用它。這是因爲,如果 api 依賴項更改了其外部 API,Gradle 會在編譯時重新編譯所有有權訪問該依賴項的模塊。因此,擁有大量的 api 依賴項會顯著增加構建時間。除非要將依賴項的 API 公開給單獨的模塊,否則庫模塊應改用 implementation 依賴項。
compileOnly provided Gradle 只會將依賴項添加到編譯類路徑(也就是說,不會將其添加到構建輸出)。如果您創建 Android 模塊時在編譯期間需要相應依賴項,但它在運行時可有可無,此配置會很有用。如果您使用此配置,那麼您的庫模塊必須包含一個運行時條件,用於檢查是否提供了相應依賴項,然後適當地改變該模塊的行爲,以使該模塊在未提供相應依賴項的情況下仍可正常運行。這樣做不會添加不重要的瞬時依賴項,因而有助於減小最終 APK 的大小。注意:您不能將 compileOnly 配置與 AAR 依賴項配合使用。
runtimeOnly apk Gradle 只會將依賴項添加到構建輸出,以便在運行時使用。也就是說,不會將其添加到編譯類路徑。
annotationProcessor compile 如需添加對作爲註釋處理器的庫的依賴關係,您必須使用 annotationProcessor 配置將其添加到註釋處理器類路徑。這是因爲,使用此配置可以將編譯類路徑與註釋處理器類路徑分開,從而提高構建性能。如果 Gradle 在編譯類路徑上找到註釋處理器,則會禁用避免編譯功能,這樣會對構建時間產生負面影響(Gradle 5.0 及更高版本會忽略在編譯類路徑上找到的註釋處理器)。如果 JAR 文件包含以下文件,則 Android Gradle 插件會假定依賴項是註釋處理器:META-INF/services/javax.annotation.processing.Processor。如果插件檢測到編譯類路徑上包含註釋處理器,則會生成構建錯誤。
lintChecks 使用此配置可以添加您希望 Gradle 在構建項目時執行的 lint 檢查.注意:使用 Android Gradle 插件 3.4.0 及更高版本時,此依賴項配置不再將 lint 檢查打包在 Android 庫項目中。如需將 lint 檢查依賴項包含在 AAR 庫中,請使用下面介紹的 lintPublish 配置。
lintPublish 在 Android 庫項目中使用此配置可以添加您希望 Gradle 編譯成 lint.jar 文件並打包在 AAR 中的 lint 檢查。這會使得使用 AAR 的項目也應用這些 lint 檢查。如果您之前使用 lintChecks 依賴項配置將 lint 檢查包含在已發佈的 AAR 中,則需要遷移這些依賴項以改用 lintPublish 配置。

1.爲構建變體添加依賴

以上配置會將依賴項應用於所有構建變體。如果您只想爲特定的構建變體源代碼集或測試源代碼集聲明依賴項,則必須將配置名稱的首字母大寫,並在其前面加上構建變體或測試源代碼集的名稱作爲前綴。

例如,如需只向“free”產品變種添加 implementation 依賴項(使用遠程二進制文件依賴項),請使用如下所示的代碼:

dependencies {
    freeImplementation 'com.google.firebase:firebase-ads:9.8.0'
}

不過,如果您想爲將產品變種和構建類型組合在一起的變體添加依賴項,就必須在 configurations 代碼塊中初始化配置名稱。以下示例向“freeDebug”構建變體添加了 runtimeOnly 依賴項(使用本地二進制文件依賴項):

configurations {
    // 爲freeDebugRuntimeOnly依賴項初始化一個佔位符配置。
    freeDebugRuntimeOnly {}
}

dependencies {
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
}

如需爲本地測試和插樁測試添加 implementation 依賴項,請使用如下所示的代碼:

dependencies {
    // 僅爲本地測試添加遠程二進制依賴項。
    testImplementation 'junit:junit:4.12'

    // 僅爲已檢測的測試APK添加遠程二進制依賴項。
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

不過,某些配置在這種情況下沒有意義。例如,因爲其他模塊不能依賴於 androidTest,所以如果您使用 androidTestApi 配置,會收到以下警告:

WARNING: Configuration 'androidTestApi' is obsolete and has been replaced with
'androidTestImplementation'.

2.使用變體感知型依賴項管理機制

Android 插件 3.0.0 及更高版本包含一種新的依賴項機制,該機制可在使用庫時自動匹配變體。這意味着,應用的 debug 變體會自動使用庫的 debug 變體,依此類推。在使用變種時,這種機制也同樣適用 - 應用的 freeDebug 變體將使用庫的 freeDebug 變體。
Android 插件 3.0.0 及更高版本包含一種新的依賴項機制,該機制可在使用庫時自動匹配變體。這意味着,應用的 debug 變體會自動使用庫的 debug 變體,依此類推。在使用變種時,這種機制也同樣適用 - 應用的 freeDebug 變體將使用庫的 freeDebug 變體。

爲了讓插件準確匹配變體,您需要在無法進行直接匹配的情況下提供匹配回退機制。不妨假設您的應用配置了一個名爲“staging”的構建類型,但該應用的一個庫依賴項沒有進行相應配置。當插件嘗試構建“staging”版本的應用時,它不知道要使用哪個版本的庫,因此您將看到一條與以下內容類似的錯誤消息:

Error:Failed to resolve: Could not resolve project :mylibrary.Required by: project :app

3.解決與變體匹配相關的構建錯誤

  1. 編譯錯誤原因:您的應用包含庫依賴項不包含的構建類型
    例如,您的應用包含“staging”版本類型,但依賴項僅包含“debug”和“release”版本類型。
    解決方案:使用 matchingFallbacks 爲給定的構建類型指定替代匹配,如下所示:
    android {
    buildTypes {
        debug {}
        release {}
        staging {
            //指定後備的構建類型的排序列表,當依賴項不包含“ staging”構建類型。您可以指定一個,然後插件選擇第一個可用的構建類型添在依賴項。
            matchingFallbacks = ['debug', 'qa', 'release']
        }
    }
    

}
```

  1. 編譯錯誤原因:對於應用及其庫依賴項中均存在的給定變種維度,您的應用包含庫不包含的變種。
    例如,您的應用及其庫依賴項都包含“tier”變種維度。不過,應用中的“tier”維度包含“free”和“paid”變種,但依賴項中的同一維度僅包含“demo”和“paid”變種。
    解決方案:使用 matchingFallbacks 爲應用的“free”產品變種指定替代匹配,如下所示:
    android {
    defaultConfig{
    // 不要在defaultConfig塊中配置matchFallbacks。相反,您必須在productFlavors塊中添加他;
    }
    flavorDimensions 'tier'
    productFlavors {
        paid {
            dimension 'tier'
            // 因爲依賴項已在其依賴項中包含“付費”的形式“層級”維度,您無需提供後備列表代表“付費”變體。
        }
        free {
            dimension 'tier'
            matchingFallbacks = ['demo', 'trial']
        }
    }
    

}
```

  1. 編譯錯誤原因:庫依賴項包含您的應用不包含的變種維度
    例如,庫依賴項包含“minApi”維度的變種,但您的應用僅包含“tier”維度的變種。因此,當您要構建“freeDebug”版本的應用時,插件不知道是使用“minApi23Debug”還是“minApi18Debug”版本的依賴項。
    解決方案:在 defaultConfig 代碼塊中使用 missingDimensionStrategy 指定插件應從每個缺失維度中選擇的默認變種,如以下示例所示。您也可以替換在 productFlavors 代碼塊中的選擇,讓每一個變種都可以爲缺失維度指定一個不同的匹配策略。
    android {
    defaultConfig{
    // 指定插件應嘗試從中使用的變體的排序列表給定的尺寸下面告訴插件,當遇到包含“ minApi”維的依賴項,應選擇“ minApi18”變體。您可以包括其他變體名稱以提供對維度的後備列表進行排序。
    missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
    // 您應該爲每個屬性指定一個missingDimensionStrategy屬性存在於本地依賴項中但不存在於您的應用程序中。
    missingDimensionStrategy 'abi', 'x86', 'arm64'
    }
    flavorDimensions 'tier'
    productFlavors {
        free {
            dimension 'tier'
            // 您可以覆蓋產品變體的默認選擇通過配置另一個missingDimensionStrategy屬性進行級別用於“ minApi”維度。
            missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
        }
        paid {}
    }
    

}
```

4.排除傳遞依賴項

隨着應用的範圍不斷擴大,它可能會包含許多依賴項,包括直接依賴項和傳遞依賴項(應用中導入的庫所依賴的庫)。如需排除不再需要的傳遞依賴項,您可以使用 exclude 關鍵字,如下所示:

dependencies {
    implementation('some-library') {
        exclude group: 'com.example.imgtools', module: 'native'
    }
}

三、遠程代碼庫

當您的依賴項不是本地庫或文件樹時,Gradle 會在 build.gradle 文件的 repositories 代碼塊中指定的所有在線代碼庫中查找相關文件。各個代碼庫的列出順序決定了 Gradle 在這些代碼庫中搜索各個項目依賴項的順序。例如,如果從代碼庫 A 和 B 均可獲得某個依賴項,而您先列出了代碼庫 A,則 Gradle 會從代碼庫 A 下載該依賴項。

默認情況下,新的 Android Studio 項目會將 Google 的 Maven 代碼庫和 JCenter 指定爲項目的頂級 build.gradle 文件中的代碼庫位置,如下所示:

allprojects {
    repositories {
        google()
        jcenter()
    }
}

如果您要從 Maven 中央代碼庫獲取某些內容,則添加 mavenCentral();對於本地代碼庫,則使用 mavenLocal():

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        mavenLocal()
    }
}

或者,您也可以按如下方式聲明特定的 Maven 或 Ivy 代碼庫:

allprojects {
    repositories {
        maven {
            url "https://repo.example.com/maven2"
        }
        maven {
            url "file://local/repo/"
        }
        ivy {
            url "https://repo.example.com/ivy"
        }
    }
}

四、依賴項順序

依賴項的列出順序指明瞭每個庫的優先級:第一個庫的優先級高於第二個,第二個庫的優先級高於第三個,依此類推。在合併資源或將清單元素從庫中合併到應用中時,此順序很重要。
例如,如果您的項目聲明以下內容:

  • 依賴 LIB_A 和 LIB_B(按此順序)
  • LIB_A 依賴於 LIB_C 和 LIB_D(按此順序)
  • LIB_B 也依賴於 LIB_C
    那麼,扁平型依賴項順序將如下所示:
  1. LIB_A
  2. LIB_D
  3. LIB_B
  4. LIB_C
    這可以確保 LIB_A 和 LIB_B 都可以替換 LIB_C;並且 LIB_D 的優先級仍高於 LIB_B,因爲 LIB_A(依賴前者)的優先級高於 LIB_B。

五、查看模塊依賴項

一些直接依賴項可能具有自己的依賴項。此類依賴項稱爲“傳遞依賴項”。Gradle 將會自動爲您收集並添加這些傳遞依賴項,無需您手動逐一加以聲明。Android Plugin for Gradle 提供了一項任務,用來列出 Gradle 爲給定模塊解析的依賴項。

對於每個模塊,報告還會根據構建變體、測試源代碼集和類路徑對依賴項進行分組。下面是一個應用模塊的依賴項示例報告,其中按該模塊的調試構建變體的運行時類路徑和該模塊的插樁測試源代碼集的編譯類路徑對依賴項進行了分組。
如需運行該任務,請按以下步驟操作:

  1. 依次選擇 View > Tool Windows > Gradle(或點擊工具窗口欄中的 Gradle 圖標)。
  2. 依次展開 AppName > Tasks > android,然後雙擊 androidDependencies。Gradle 執行該任務後,系統應該會打開 Run 窗口以顯示輸出。
    顯示如下:
debugRuntimeClasspath - Dependencies for runtime/packaging
+--- :mylibrary (variant: debug)
+--- com.google.android.material:material:1.0.0@aar
+--- androidx.appcompat:appcompat:1.0.2@aar
+--- androidx.constraintlayout:constraintlayout:1.1.3@aar
+--- androidx.fragment:fragment:1.0.0@aar
+--- androidx.vectordrawable:vectordrawable-animated:1.0.0@aar
+--- androidx.recyclerview:recyclerview:1.0.0@aar
+--- androidx.legacy:legacy-support-core-ui:1.0.0@aar
...

debugAndroidTest
debugAndroidTestCompileClasspath - Dependencies for compilation
+--- androidx.test.ext:junit:1.1.0@aar
+--- androidx.test.espresso:espresso-core:3.1.1@aar
+--- androidx.test:runner:1.1.1@aar
+--- junit:junit:4.12@jar
...

六、修復依賴項解析錯誤

當您嚮應用項目添加多個依賴項時,這些直接和傳遞依賴項可能會相互衝突。Android Gradle 插件會嘗試妥善解決這些衝突,但有些衝突可能會導致編譯時或運行時錯誤。

爲幫助您調查是哪些依賴項導致了錯誤,請如上檢查您的應用的依賴項樹,從中查找多次出現的依賴項或版本衝突的依賴項。

如果無法輕鬆識別重複的依賴項,請嘗試使用 Android Studio 的界面搜索包含重複類的依賴項,具體操作步驟如下:

  1. 從菜單欄中依次選擇 Navigate > Class。
  2. 在彈出式搜索對話框中,確保已勾選 Include non-project items 旁邊的框。
  3. 輸入出現在構建錯誤中的類的名稱。
  4. 檢查結果以查找包含該類的依賴項。

下面幾部分介紹您可能會遇到的不同類型的依賴項解析錯誤及其修復方法。

1.修復重複類錯誤

如果某個類多次出現在運行時類路徑上,您會收到一條與以下內容類似的錯誤:

Program type already present com.example.MyClass

此錯誤通常是下列其中一種情況所致:

  • 二進制文件依賴項包含一個庫,該庫也作爲直接依賴項包含在您的應用中。例如,您的應用聲明直接依賴於庫 A 和庫 B,但庫 A 已在其二進制文件中包含庫 B。

    • 如需解決此問題,請取消將庫 B 作爲直接依賴項。
  • 您的應用的本地二進制文件依賴項和遠程二進制文件依賴項是同一個庫。

    • 如需解決此問題,請移除其中一個二進制文件依賴項。

2.解決類路徑之間的衝突

當 Gradle 解析編譯類路徑時,會先解析運行時類路徑,然後使用所得結果確定應添加到編譯類路徑的依賴項版本。換句話說,運行時類路徑決定了下游類路徑上完全相同的依賴項所需的版本號。

應用的運行時類路徑還決定了 Gradle 需要對應用的測試 APK 的運行時類路徑中的匹配依賴項使用的版本號。圖 1 說明了類路徑的層次結構。
圖 1. 必須根據此層次結構匹配出現在多個類路徑中的依賴項的版本號

例如,當應用使用 implementation 依賴項配置加入某個依賴項的一個版本,而庫模塊使用 runtimeOnly 配置加入該依賴項的另一個版本時,就可能會發生多個類路徑中出現同一依賴項的不同版本的衝突。

在解析對運行時和編譯時類路徑的依賴關係時,Android Gradle 插件 3.3.0 及更高版本會嘗試自動解決某些下游版本衝突。例如,如果運行時類路徑包含庫 A 版本 2.0,而編譯類路徑包含庫 A 版本 1.0,則插件會自動將對編譯類路徑的依賴關係更新爲庫 A 版本 2.0,以避免錯誤。

不過,如果運行時類路徑包含庫 A 版本 1.0,而編譯類路徑包含庫 A 版本 2.0,插件不會將對編譯類路徑的依賴關係降級爲庫 A 版本 1.0,您仍會收到一條與以下內容類似的錯誤:

Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.

如需解決此問題,請執行以下某項操作:

  • 將所需版本的依賴項作爲 api 依賴項添加到庫模塊。也就是說,只有庫模塊聲明相應依賴項,但應用模塊也能以傳遞方式訪問其 API。
  • 或者,您也可以同時在兩個模塊中聲明相應依賴項,但應確保每個模塊使用的版本相同。不妨考慮配置項目全局屬性,以確保每個依賴項的版本在整個項目中保持一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章