Android 開發使用 Gradle 配置構建庫模塊的工做方式

Android 開發過程當中,咱們不可避免地須要引入其餘人的工做成果。減小重複「造輪子」的時間,投入到更有意義的核心任務當中。

Android 庫模塊在結構上與 Android 應用模塊相同。提供構建應用所需的一切內容,包括源代碼(src)、資源文件(res)和 Android 清單文件(AndroidManifest.xml)。java

Android Studio IDE 提供選項建立庫模塊:android

  1. 在項目中建立一個新的庫模塊(New Module
  2. 將應用模塊轉換爲庫模塊(因二者結構基本相同)

若是現有的應用模塊包含但願重用的全部代碼,能夠經過修改 build.gradle文件:api

// apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
  • Android 庫模塊編譯產物爲 AAR,須要做爲其餘應用模塊依賴項使用。
  • Android 應用模塊編譯產物爲 APK,設備上能夠直接運行。

Android AAR 相似 Java JAR,除了類文件還能夠包含 Android 資源和一個清單配置文件(AndroidManifest.xml)。app

導入本地外部模塊

導入本地的外部模塊(e.g. Project B b module)到當前主項目中(e.g. Project A)。
Project B b module 一般爲庫模塊,咱們須要在另外一個 Project A 應用模塊中使用它。ide

Android Studio IDE 提供選項以依賴項形式來添加庫:gradle

  1. 添加已編譯的 AAR(或 JAR)文件(Import .JAR/.AAR Package
  2. 將庫模塊導入到您的項目中(Import Module

二者區別以下:ui

  • 庫模塊導入方式,將會複製代碼到其餘項目:
    Project A 目錄下出現 Project B b module 的拷貝
  • 庫模塊導入以後容許編輯庫代碼,可是修改只對當前項目生效:
    Project A 目錄下修改 b module 不會影響到 Project B b module

在現實開發過程當中,咱們但願維護一個統一版本的庫模塊,這樣一來庫模塊的更新就會同步給全部依賴於它的項目:
Project A、Project C、Project D 都依賴於 Project B b module,庫模塊 b 的修改會同步到各個項目。插件

  • 庫模塊導入方式顯然沒法完成任務,由於其是經過拷貝方式導入。
  • 添加已編譯的 AAR(或 JAR)文件能夠完成任務,可是依然須要人工切換項目點選操做。

解決方案:
配置 gradle 經過本地相對路徑指定庫模塊文件夾,實現本地外部模塊導入。日誌

打開主項目 settings.gradle 文件導入庫:code

include ':my-library-module'
project(':my-library-module').projectDir = new File(settingsDir, '../my-library-module')

打開主項目應用模塊的 build.gradle 文件,並向 dependencies 塊中添加依賴:

dependencies {
    compile project(":my-library-module")
}

庫模塊開發注意事項

將庫模塊引用添加至您的 Android 應用模塊後,庫模塊會根據優先級的順序與應用模塊進行合併。

資源合併衝突

  • 當庫模塊與應用模塊均定義了相同資源 ID,默認使用應用模塊的資源,e.g. @string/app_name
  • 多個 AAR 庫之間發生資源 ID 衝突,根據依賴項列表順序,優先使用 dependencies 塊頂部模塊的資源

避免經常使用資源 ID 衝突的有效辦法,是在各個模塊中使用具備惟一性的前綴命名規範。

AndroidManifest 合併衝突

考慮到兼容性問題,應用模塊的 minSdkVersion 必須大於或等於庫定義的版本。
庫模塊中如若使用到僅高版本 SDK 支持的 API,將會致使應用模塊編譯失敗。
Android 在切換到 Gradle 做爲構建系統以前,經過 Manifest 設置 minSdkVersion,以後其值會被 build.gradle 文件中的值覆蓋。

Android 應用的 APK 文件中只能包含一個 AndroidManifest.xml,不過 Android Studio 項目能夠包含多個該文件(來自主應用模塊及各個庫模塊)。所以,在構建應用時,Gradle 構建會將全部清單文件(AndroidManifest.xml)合併。清單文件按照優先級從低到高合併,遵循特定規則合併各個清單文件中的全部 XML 元素 。

清單文件優先級由高到低的順序:

  1. 清單文件構建變體
  2. 應用模塊的主清單文件
  3. 所包括庫中的清單文件

多個庫存在時,則其清單優先級與依賴順序即 dependencies 塊中的順序匹配。

Manifest merger failed 示例:
android:theme 在多個 AndroidManifest.xml 被定義且值不一樣,形成合並衝突。

Project A 主項目 AndroidManifest.xml

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

Project B b Library Module AndroidManifest.xml

<application
        android:theme="Theme.AppCompat.Light.DarkActionBar">

遇到 Manifest 衝突參考 Gradle Console 給出的錯誤日誌和提示,解決衝突。
例如使用 tools:replace 方式避免屬性衝突,藉助 tools 域名空間(xmlns:tools="http://schemas.android.com/tools")設置 Manifest 的合併優先級。明確表示合併時移除低優先級 library module 中的相關屬性,使用高優先級 application module 中定義的對應屬性內容。

Project A 主項目 AndroidManifest.xml

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:replace="android:theme">

模塊依賴分析

考慮到多重嵌套依賴問題,Gradle 相似 Maven 支持傳遞依賴,即庫自己依賴於其餘庫,由此須要解決依賴之間的版本問題。

複雜的依賴關係極可能致使重複引入包,例如:support-v4support-v7 包,從而發生衝突。

多個模塊之間存在相同依賴而且發生衝突,能夠經過 exclude 語法過濾相同依賴:

// helloworld build.gradle
...
compile ('com.example.helloworld:my-library-module:1.0.0') {
    exclude group: 'com.android.support', module: 'support-v4'
    exclude group: 'com.android.support', module: 'support-v7'
}

上述方法是在主項目引入其餘庫模塊時進行過濾依賴,做爲庫模塊開發者咱們也應該考慮到其餘用戶的使用狀況。

provided 語法在建立 Android 庫模塊時很是有用,將依賴項添加到編譯過程當中,但不會添加到編譯輸出中。這樣一來減小最終 APK、AAR 產物大小,同時避免添加沒必要要依賴項。

注意:須要告知用戶此依賴項存在,由其如何決定引入依賴。

// my-library-module build.gradle
...
ext.supportLibVersion = '26.1.0'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    provided "com.android.support:appcompat-v7:${supportLibVersion}"
}

Project A 依賴 a、b、c module,同時 a、b module 又依賴於 d module,且 a、b 各自依賴的 d module 版本(version)不一致,不一樣 version 的 d module 中 API 接口如若發生改變,Project A Build/Sync 將會失敗。 例如 version 1.0 中的方法 method1,在 version 2.0 被移除將會遇到 java.lang.NoSuchMethodError

建議儘量保持依賴項 d module version 一致,或者使用不一樣 version 可是差別不大,起碼作到 API 可以通用。

經過 ./gradlew dependencies 命令能夠查看依賴關係,附加參數能夠查看指定類型、模塊依賴關係:

./gradlew my-library-module:dependencies --configuration archives
archives - Configuration for archive artifacts.
+--- com.android.support:recyclerview-v7:26.1.0
|    +--- com.android.support:support-annotations:26.1.0
|    +--- com.android.support:support-compat:26.1.0
|    |    +--- com.android.support:support-annotations:26.1.0
|    |    \--- android.arch.lifecycle:runtime:1.0.0
|    |         +--- android.arch.lifecycle:common:1.0.0
|    |         \--- android.arch.core:common:1.0.0
|    \--- com.android.support:support-core-ui:26.1.0
|         +--- com.android.support:support-annotations:26.1.0
|         \--- com.android.support:support-compat:26.1.0 (*)

另外 Android 項目可使用 ./gradlew androidDependencies


另外,考慮到構建問題,庫模塊使用的 gradle 插件與應用模塊儘可能保持一致。
Android Gradle Plugin 版本不一致可能會影響到依賴項配置語法:

New configuration Deprecated configuration
implementation compile
api compile
compileOnly provided
runtimeOnly apk

引用

建立 Android 庫
Add build dependencies
合併多個清單文件

相關文章
相關標籤/搜索