發(fā)布?Android?library?到?Maven?解析
前言
了解一下將 Android library 發(fā)布到中央倉庫(比如 Maven Center,jitpack) 的過程中關(guān)于一些細(xì)節(jié)的疑惑。比如我們到底發(fā)布了啥?只有 xxx.aar 文件嗎?代碼中依賴的三方庫又是怎么處理的?
疑惑
關(guān)于如何將一個(gè)功能完善的 Android Library 發(fā)布到 Maven Center 或者類似的中央倉庫,已經(jīng)是非常小兒科的東西了,網(wǎng)上有很多資料可以參考。
但是關(guān)于發(fā)布的內(nèi)容,發(fā)布過程的一些細(xì)節(jié),仍然有很多問題值得我們思考。
- 通過配置 publish 功能插件,執(zhí)行發(fā)布任務(wù)之后,我們到底發(fā)布了啥?只有 aar 文件嗎?
- 本地 library 依賴的三方庫是怎么處理的?是編譯到我們發(fā)布的包里了嗎?
- 本地 library 依賴的三方庫,dependencies 閉包下 libs 目錄和直接
implementation
三方庫最終會有什么差異嗎? - 資源文件是怎么處理的? assets 文件夾下的內(nèi)容會發(fā)生什么?
- so 庫文件呢,會發(fā)生什么?
- 代碼的混淆配置怎么處理?library 下的
proguard-rules.pro
和consumer-rules.pro
有啥用?
如果你對上面的這些內(nèi)容了如指掌,那么下面的東西也就不用看了,可以直接叉掉了。
解惑
關(guān)于如何給 library 通過 gradle 進(jìn)行發(fā)布相關(guān)的配置,相關(guān)的細(xì)節(jié)就不展開說了,可以參考的文章實(shí)在是太多了。
為了方便演示,本文直接使用本地倉庫。當(dāng)然你也可以通過 Nexux 搭建本地私服,有興趣的同學(xué)可以參考 Android 使用maven publish插件發(fā)布產(chǎn)物(aar)流程實(shí)踐。
這里將發(fā)布配置統(tǒng)一到一個(gè) publish.gradle
文件當(dāng)中.
發(fā)布配置
apply plugin: 'maven-publish' def GROUP_ID = "com.engineer.third" def ARTIFACT_ID = "thirdlib" def VERSION = "1.0.0" // 上傳源碼的 task task sourceJar(type: Jar) { from android.sourceSets.main.java.srcDirs archiveClassifier = "sources" } afterEvaluate { publishing { // 配置maven 倉庫 repositories { RepositoryHandler handler -> handler.mavenLocal() handler.maven { url "${rootDir}/local_repo/" } } publications { PublicationContainer publicationContainer -> debug(MavenPublication) { from components.debug artifact sourceJar // 上傳源碼 groupId = GROUP_ID artifactId = ARTIFACT_ID version = "${VERSION}_DEBUG" } release(MavenPublication) { from components.release artifact sourceJar // 上傳源碼 groupId = GROUP_ID artifactId = ARTIFACT_ID version = VERSION } } } }
為了方便,我們配置了 ${rootDir}/local_repo/
作為本地倉庫,本地倉庫默認(rèn)路徑是 /Users/username/.m2/repository
。其實(shí),我們可以直接使用這個(gè)本地倉庫,在 build.gradle 中添加 maven 地址就可以了。
allprojects { repositories { maven { url "${rootDir}/local_repo/" } google() jcenter() mavenCentral() } }
這樣我們就可以在本地進(jìn)行模擬發(fā)布和依賴 library 的功能。畢竟,倉庫地址只是個(gè)地址而已。
這樣在 library 依賴這個(gè)配置文件 apply from: file("publish.gradle")
后,我們便可以在 gradle task 中看到發(fā)布任務(wù)了。
發(fā)布內(nèi)容
通過雙擊圖中的 publish
任務(wù)或者是命令行執(zhí)行 gradlew :thirdlib:publish
就可以開始執(zhí)行發(fā)布任務(wù),發(fā)布任務(wù)成功后我們就可以到 ${rootDir}/local_repo/
查看一下到底有哪些產(chǎn)物。
有哪些內(nèi)容
發(fā)布產(chǎn)物看著很多,但其實(shí)只有這么幾類
- aar 文件
- module 文件
- pom 文件
- source.jar
其余都是這些文件的數(shù)字簽名。
用 Android Studio 看一下 aar 的內(nèi)容。
可以看到 library 源碼中的 xxx.so 和 assets 目錄下的文件會按照編譯時(shí)的配置和內(nèi)容,原封不動的打包到 aar 中。即便是沒有使用到的內(nèi)容。
aar 的內(nèi)容就是我們的代碼了,source.jar 是源代碼,方便依賴 library 的時(shí)候進(jìn)行查看。那么剩下的 module 文件和 pom 文件有什么作用呢?我們結(jié)合剩下的問題來解決這個(gè)疑問。
依賴的規(guī)則
關(guān)于依賴,一般有兩種:
- 將 xxx.jar 或 xxx.aar 文件放到本地 libs 目錄下,然后通過
implementation fileTree(include: ['*.jar','*.aar'], dir: 'libs')
進(jìn)行依賴。 - 通過
implementation 'com.google.code.gson:gson:2.9.0'
這種直接聲明中央倉庫group_id:artifact_id:version
的方式進(jìn)行依賴。
這里的情況比較復(fù)雜,分開說明一下。首先看本地依賴直接依賴 libs 包的內(nèi)容。
本地依賴
aar
直接從 libs 包依賴三方組件的時(shí)候,如果是 aar 包,那么為了編譯生成 library 自己的 aar ,則只能用 compileOnly fileTree(include: ['*.aar'], dir: 'libs')
的方式。否則會報(bào)錯:
Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken because the classes and Android resources from any local .aar file dependencies would not be packaged in the resulting AAR.
至于為什么有這種限制官方的報(bào)錯信息已經(jīng)說明原因了。顯而易見,本地依賴的 aar 包是無法打進(jìn)最終產(chǎn)物的。 那么如果必須要用到這個(gè) aar 該怎么辦呢?兩種辦法,一是將 aar 發(fā)布到中央倉庫,進(jìn)行遠(yuǎn)程依賴;二是哪里缺這個(gè) aar ,就在哪里直接進(jìn)行本地依賴。
jar
jar 類型的文件要分情況:
compilyOnly
依賴,那么不會輸出到最終產(chǎn)物中。implementation
和api
依賴,將在最終 aar 文件的 libs 目錄下保留相應(yīng)的依賴。
可以看到源碼中 libs 目錄下的兩個(gè) .jar 文件最終都輸出到了 aar 文件中。這里需要注意的是,class.jar 中并沒有包含 libs 目錄下 jar 文件的 *.class
文件,只是一些配置文件。
中央倉庫的依賴
這里首先需要明確一點(diǎn)是,只要是通過聲明中央倉庫 group_id:artifact_id:version
的方式進(jìn)行依賴,那么所依賴的內(nèi)容一定不會打包到最終的 aar 文件的。這么做是合理的,也是必須得,否則這 Android 的依賴管理將是一場噩夢。試想一下,如果所有依賴的東西,都可以打到一個(gè)包里,那如果發(fā)生沖突了,豈不是要解到天荒地老。
通過聲明中央倉庫 group_id:artifact_id:version
的方式進(jìn)行依賴的內(nèi)容,在編譯時(shí)必然是需要的,那么依賴這個(gè) library 的使用方,又該如何獲取到這些依賴呢?這就要靠前面提到的 pom 文件了。
這里我們先比較一下,dependencies 各種配置一下,pom 文件的內(nèi)容。
- api
dependencies { compileOnly fileTree(include: ['*.aar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs') compileOnly 'com.squareup.radiography:radiography:2.4.1' implementation 'androidx.appcompat:appcompat:1.5.1' api 'com.google.android.material:material:1.6.1' }
<?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- This module was also published with a richer model, Gradle metadata, --> <!-- which should be used instead. Do not delete the following line which --> <!-- is to indicate to Gradle or any Gradle module metadata file consumer --> <!-- that they should prefer consuming it instead. --> <!-- do_not_remove: published-with-gradle-metadata --> <modelVersion>4.0.0</modelVersion> <groupId>com.engineer.third</groupId> <artifactId>thirdlib</artifactId> <version>1.0.0</version> <packaging>aar</packaging> <dependencies> <dependency> <groupId>com.google.android.material</groupId> <artifactId>material</artifactId> <version>1.6.1</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-stdlib-jdk8</artifactId> <version>1.6.10</version> <scope>compile</scope> </dependency> <dependency> <groupId>androidx.appcompat</groupId> <artifactId>appcompat</artifactId> <version>1.5.1</version> <scope>runtime</scope> </dependency> </dependencies> </project>
看到 compile
這個(gè)在使用 Android Studio 2.x 版本時(shí)的常見的配置,是不是有種似曾相識的感覺??梢钥吹?/p>
- 本地依賴的配置不會出現(xiàn)在 pom 文件中
- compileOnly 依賴的內(nèi)容,不會出現(xiàn)在 pom 文件中
- implementation 在 pom 文件中 scope 變成了 runtime
- api 在 pom 文件中 scope 變成了 compile
這些都是符合預(yù)期的。關(guān)于 pom 文件是什么以及 scope 的作用,這里就不解釋了,網(wǎng)上有很多相關(guān)的科普,很容易理解的。
當(dāng)我們在 gradle 中配置一個(gè)三方庫依賴的時(shí)候,在 Android Studio 底部的信息欄你應(yīng)該大概率看到過 download xxx.pom 文件的信息。gradle 就是通過這個(gè) pom 文件的信息,明確當(dāng)前依賴的 library 自身還依賴了那些內(nèi)容,這就是所謂的依賴傳遞。
沖突
關(guān)于依賴,其實(shí)還有點(diǎn)其他內(nèi)容。如果你是一個(gè)有追求的開發(fā)者,有代碼潔癖。一定希望自己輸出的東西越干凈越好,使用者用起來越簡單、問題越少越好。比如你聲明的依賴和使用者聲明的依賴沖突了怎么辦?人家用的 glide 就是版本就是 3.x ,okhttp 版本就是 3.10.x ,而且還不能隨意變動,你比人家高該怎么辦? 這時(shí)候?qū)ν廨敵龅臅r(shí)候就要做好選擇,到底是用 compileOnly
還是 api
。也可以使用 exclude 排除一些自身沒有用到的內(nèi)容,既可以排除可能造成沖突的依賴,又可以稍微加快一定編譯速度,何樂而不為呢。
比如像下面這樣:
implementation("com.squareup.retrofit2:retrofit:2.9.0") { exclude group: "com.squareup.okhttp3", module: "okhttp" exclude module: "okio" }
本身只是使用 retrofit 進(jìn)行了一些簡單的封裝,完全可以將 okhttp 相關(guān)的內(nèi)容排除掉。盡可能避免給使用方帶來沖突的可能性。
<dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>retrofit</artifactId> <version>2.9.0</version> <scope>runtime</scope> <exclusions> <exclusion> <artifactId>okio</artifactId> <groupId>*</groupId> </exclusion> <exclusion> <artifactId>okhttp</artifactId> <groupId>com.squareup.okhttp3</groupId> </exclusion> </exclusions> </dependency> </dependencies>
可以看到,pom 文件會很自然的記錄這種優(yōu)化,使用方在依賴的時(shí)候,也會排除這些依賴,自然不會莫名其妙的引入一些意料之外又用不到的東西。
當(dāng)然,這種沖突也不是致命的,使用者也可以自己配置 exclude 移除沖突的依賴項(xiàng),或者是主動聲明關(guān)閉依賴傳遞。
module 文件的作用暫時(shí)沒理解出來,有知道的同學(xué)可以科普一下 ??囧
混淆配置
最后再說一下混淆配置。一般情況下,當(dāng)我們新建一個(gè) library 類型的 Android Module 時(shí),會自動創(chuàng)建 proguard-rules.pro
和 consumer-rules.pro
這兩個(gè)文件。為什么需要兩個(gè),各自有什么用呢?
其實(shí)也很簡單,通俗來說:
proguard-rules.pro
是 library 自己用的consumer-rules.pro
是給依賴這個(gè) library 的使用者用的。
proguard-rules.pro
中配置當(dāng)前 library 中那些內(nèi)容需要混淆,那些內(nèi)容需要保留。比如以 thridlib library 為例。
分別對這兩個(gè)文件進(jìn)行配置,這里簡單起見,隨便配置一下,主要是說明問題。
proguard-rules.pro
-keep class com.engineer.third.internal.NativeMethodsFactory {*;}
consumer-rules.pro
-keep class com.engineer.third.** {*;}
我們看一下打包輸出的 aar:
可以看到 proguard-rules.pro
配置的內(nèi)容對當(dāng)前 library 生效了(當(dāng)然這個(gè)配置完全可以不用寫,Android 自帶的 proguard 規(guī)則會 keep native 方法及類,這里只是舉例)
這個(gè)就比較有意思了 consumer-rules.pro
的內(nèi)容變成了 proguard.txt
。那么這個(gè)文件,又有什么用呢?這里就牽扯到另一個(gè)知識點(diǎn)了,Android 在輸出 release apk 進(jìn)行混淆的過程中,會收集每個(gè)依賴庫中的混淆配置,合并到一起。生成一個(gè)最終的混淆配置文件,最終會基于這個(gè)文件進(jìn)行混淆處理。這個(gè)總的混淆配置文件一般在 app/build/outputs/mapping/falvor/configuration.txt
中,其中就可以看到各個(gè)依賴庫的混淆說明。
這就是 library 中混淆配置的用法。
小結(jié)
本文通過總結(jié)發(fā)布 library 和平時(shí)使用三方 library 時(shí)的一些疑問和困惑,就普通情況進(jìn)行了相關(guān)的梳理。這樣下次遇到相關(guān)問題時(shí)就有理可循了,不會再抓蝦
到此這篇關(guān)于發(fā)布 Android library 到 Maven 解析的文章就介紹到這了,更多相關(guān)Android library Maven內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android使用ContentProvider初始化SDK庫方案小結(jié)
這篇文章主要介紹了Android使用ContentProvider初始化SDK庫方案總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04Android Scroll實(shí)現(xiàn)彈性滑動_列表下拉彈性滑動的示例代碼
下面小編就為大家分享一篇Android Scroll實(shí)現(xiàn)彈性滑動_列表下拉彈性滑動的示例代碼,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01Android獲取設(shè)備CPU核數(shù)、時(shí)鐘頻率以及內(nèi)存大小的方法
這篇文章主要介紹了Android獲取設(shè)備CPU核數(shù)、時(shí)鐘頻率以及內(nèi)存大小的方法,涉及Android針對系統(tǒng)硬件相關(guān)操作技巧,需要的朋友可以參考下2016-07-07學(xué)習(xí)Android開發(fā)之RecyclerView使用初探
Android開發(fā)學(xué)習(xí)之路的第一課RecyclerView使用初探,感興趣的小伙伴們可以參考一下2016-07-07Android?Jetpack庫剖析之ViewModel組件篇
這篇文章主要介紹了Android?Jetpack架構(gòu)組件?ViewModel詳解,ViewModel類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)存在,ViewModel類旨在以注重生命周期的方式存儲和管理界面相關(guān)的數(shù)據(jù)。感興趣可以來學(xué)習(xí)一下2022-07-07深入理解Android熱修復(fù)技術(shù)原理之so庫熱修復(fù)技術(shù)
通常情況下,大多數(shù)人希望android下熱補(bǔ)丁方案能夠做到補(bǔ)丁的全方位修復(fù),包括類修復(fù)/資源修復(fù)/so庫的修復(fù)。 這里主要介紹熱補(bǔ)丁之so庫修復(fù)思路2021-06-06android ListView內(nèi)數(shù)據(jù)的動態(tài)添加與刪除實(shí)例代碼
ListView內(nèi)數(shù)據(jù)的動態(tài)添加與刪除2013-03-03Android實(shí)現(xiàn)授權(quán)訪問網(wǎng)頁的方法
這篇文章主要介紹了Android實(shí)現(xiàn)授權(quán)訪問網(wǎng)頁的方法,需要的朋友可以參考下2014-07-07Android 使用AsyncTask實(shí)現(xiàn)斷點(diǎn)續(xù)傳
這篇文章主要介紹了Android 使用AsyncTask實(shí)現(xiàn)斷點(diǎn)續(xù)傳的實(shí)例代碼,非常不錯,具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-05-05