一文徹底搞清 Gradle 依賴

之前對(duì)Android Gradle構(gòu)建的依賴一直傻傻分不清,這段時(shí)間正好接入集團(tuán)的一個(gè)二方庫(kù),踩了很多坑,也順帶把Gradle依賴這塊搞清楚了,主要整理了下Gradle依賴的類型、依賴配置、如何查看依賴、依賴沖突如何解決。

依賴類型

dependencies DSL標(biāo)簽是標(biāo)準(zhǔn)Gradle API中的一部分,而不是Android Gradle插件的特性,所以它不屬于android標(biāo)簽。

依賴有三種方式,如下面的例子:

apply plugin: 'com.android.application'

android { ... }

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'
}

本地library模塊依賴

implementation project(":mylibrary")

這種依賴方式是直接依賴本地庫(kù)工程代碼的(需要注意的是,mylibrary的名字必須匹配在settings.gradle中include標(biāo)簽下定義的模塊名字)。

本地二進(jìn)制依賴

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

這種依賴方式是依賴工程中的 module_name/libs/目錄下的Jar文件(注意Gradle的路徑是相對(duì)于build.gradle文件來(lái)讀取的,所以上面是這樣的相對(duì)路徑)。

如果只想依賴單個(gè)特定本地二進(jìn)制庫(kù),可以如下配置:

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

遠(yuǎn)程二進(jìn)制依賴

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

上面是簡(jiǎn)寫(xiě)的方式,這種依賴完整的寫(xiě)法如下:

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

group、name、version共同定位一個(gè)遠(yuǎn)程依賴庫(kù)。需要注意的點(diǎn)是,version最好不要寫(xiě)成"12.3+"這種方式,除非有明確的預(yù)期,因?yàn)榉穷A(yù)期的版本更新會(huì)帶來(lái)構(gòu)建問(wèn)題。遠(yuǎn)程依賴需要在repositories標(biāo)簽下聲明遠(yuǎn)程倉(cāng)庫(kù),例如jcenter()、google()、maven倉(cāng)庫(kù)等。

依賴配置
目前Gradle版本支持的依賴配置有:implementation、api、compileOnly、runtimeOnly和annotationProcessor,已經(jīng)廢棄的配置有:compile、provided、apk、providedCompile。此外依賴配置還可以加一些配置項(xiàng),例如AndroidTestImplementation、debugApi等等。

常用的是implementation、api、compileOnly三個(gè)依賴配置,含義如下:

implementation
與compile對(duì)應(yīng),會(huì)添加依賴到編譯路徑,并且會(huì)將依賴打包到輸出(aar或apk),但是在編譯時(shí)不會(huì)將依賴的實(shí)現(xiàn)暴露給其他module,也就是只有在運(yùn)行時(shí)其他module才能訪問(wèn)這個(gè)依賴中的實(shí)現(xiàn)。使用這個(gè)配置,可以顯著提升構(gòu)建時(shí)間,因?yàn)樗梢詼p少重新編譯的module的數(shù)量。建議,盡量使用這個(gè)依賴配置。

api
與compile對(duì)應(yīng),功能完全一樣,會(huì)添加依賴到編譯路徑,并且會(huì)將依賴打包到輸出(aar或apk),與implementation不同,這個(gè)依賴可以傳遞,其他module無(wú)論在編譯時(shí)和運(yùn)行時(shí)都可以訪問(wèn)這個(gè)依賴的實(shí)現(xiàn),也就是會(huì)泄漏一些不應(yīng)該不使用的實(shí)現(xiàn)。舉個(gè)例子,A依賴B,B依賴C,如果都是使用api配置的話,A可以直接使用C中的類(編譯時(shí)和運(yùn)行時(shí)),而如果是使用implementation配置的話,在編譯時(shí),A是無(wú)法訪問(wèn)C中的類的。

compileOnly
與provided對(duì)應(yīng),Gradle把依賴加到編譯路徑,編譯時(shí)使用,不會(huì)打包到輸出(aar或apk)。這可以減少輸出的體積,在只在編譯時(shí)需要,在運(yùn)行時(shí)可選的情況,很有用。

runtimeOnly
與apk對(duì)應(yīng),gradle添加依賴只打包到APK,運(yùn)行時(shí)使用,但不會(huì)添加到編譯路徑。這個(gè)沒(méi)有使用過(guò)。

annotationProcessor
與compile對(duì)應(yīng),用于注解處理器的依賴配置,這個(gè)沒(méi)用過(guò)。

查看依賴樹(shù)
可以查看單個(gè)module或者這個(gè)project的依賴,通過(guò)運(yùn)行依賴的Gradle任務(wù),如下:

1、View -> Tools Windows -> Gradle(或者點(diǎn)擊右側(cè)的Gradle欄);
2、展開(kāi) AppName -> Tasks -> android,然后雙擊運(yùn)行AndroidDependencies。運(yùn)行完,就會(huì)在Run窗口打出依賴樹(shù)了。

依賴沖突解決
隨著很多依賴加入到項(xiàng)目中,難免會(huì)出現(xiàn)依賴沖突,出現(xiàn)依賴沖突如何解決?

定位沖突

依賴沖突可能會(huì)報(bào)類似下面的錯(cuò)誤:

Program type already present com.example.MyClass

通過(guò)查找類的方式(command + O)定位到?jīng)_突的依賴,進(jìn)行排除。

如何排除依賴

1、dependencies中排除(細(xì)粒度)

compile('com.taobao.android:accs-huawei:1.1.2@aar') {
        transitive = true
        exclude group: 'com.taobao.android', module: 'accs_sdk_taobao'
}

2、全局配置排除

configurations {
    compile.exclude module: 'cglib'
    //全局排除原有的tnet jar包與so包分離的配置,統(tǒng)一使用aar包中的內(nèi)容
    all*.exclude group: 'com.taobao.android', module: 'tnet-jni'
    all*.exclude group: 'com.taobao.android', module: 'tnet-so'
}

3、禁用依賴傳遞

compile('com.zhyea:ar4j:1.0') {
    transitive = false
}

configurations.all {
    transitive = false
}

還可以在單個(gè)依賴項(xiàng)中使用@jar標(biāo)識(shí)符忽略傳遞依賴:

compile 'com.zhyea:ar4j:1.0@jar'

4、強(qiáng)制使用某個(gè)版本

如果某個(gè)依賴項(xiàng)是必需的,而又存在依賴沖突時(shí),此時(shí)沒(méi)必要逐個(gè)進(jìn)行排除,可以使用force屬性標(biāo)識(shí)需要進(jìn)行依賴統(tǒng)一。當(dāng)然這也是可以全局配置的:

compile('com.zhyea:ar4j:1.0') {
    force = true
}

configurations.all {
    resolutionStrategy {
        force 'org.hamcrest:hamcrest-core:1.3'
    }
}

5、在打包時(shí)排除依賴

先看一個(gè)示例:

task zip(type: Zip) {
    into('lib') {
        from(configurations.runtime) {
            exclude '*unwanted*', '*log*'
        }
    }
    into('') {
        from jar
        from 'doc'
    }
}

代碼表示在打zip包的時(shí)候會(huì)過(guò)濾掉名稱中包含“unwanted”和“l(fā)og”的jar包。這里調(diào)用的exclude方法的參數(shù)和前面的例子不太一樣,前面的參數(shù)多是map結(jié)構(gòu),這里則是一個(gè)正則表達(dá)式字符串。

也可以使用在打包時(shí)調(diào)用include方法選擇只打包某些需要的依賴項(xiàng):

task zip(type: Zip) {
    into('lib') {
        from(configurations.runtime) {
            include '*ar4j*', '*spring*'
        }
    }
    into('') {
        from jar
        from 'doc'
    }
}

主要是使用dependencies中排除和全局配置排除。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容