網站首頁 編程語言 正文
前言
了解一下將 Android library 發布到中央倉庫(比如 Maven Center,jitpack) 的過程中關于一些細節的疑惑。比如我們到底發布了啥?只有 xxx.aar 文件嗎?代碼中依賴的三方庫又是怎么處理的?
疑惑
關于如何將一個功能完善的 Android Library 發布到 Maven Center 或者類似的中央倉庫,已經是非常小兒科的東西了,網上有很多資料可以參考。
但是關于發布的內容,發布過程的一些細節,仍然有很多問題值得我們思考。
- 通過配置 publish 功能插件,執行發布任務之后,我們到底發布了啥?只有 aar 文件嗎?
- 本地 library 依賴的三方庫是怎么處理的?是編譯到我們發布的包里了嗎?
- 本地 library 依賴的三方庫,dependencies 閉包下 libs 目錄和直接?
implementation
?三方庫最終會有什么差異嗎? - 資源文件是怎么處理的? assets 文件夾下的內容會發生什么?
- so 庫文件呢,會發生什么?
- 代碼的混淆配置怎么處理?library 下的?
proguard-rules.pro
?和?consumer-rules.pro
?有啥用?
如果你對上面的這些內容了如指掌,那么下面的東西也就不用看了,可以直接叉掉了。
解惑
關于如何給 library 通過 gradle 進行發布相關的配置,相關的細節就不展開說了,可以參考的文章實在是太多了。
為了方便演示,本文直接使用本地倉庫。當然你也可以通過 Nexux 搭建本地私服,有興趣的同學可以參考?Android?使用maven?publish插件發布產物(aar)流程實踐。
這里將發布配置統一到一個?publish.gradle
?文件當中.
發布配置
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/
?作為本地倉庫,本地倉庫默認路徑是?/Users/username/.m2/repository
。其實,我們可以直接使用這個本地倉庫,在 build.gradle 中添加 maven 地址就可以了。
allprojects { repositories { maven { url "${rootDir}/local_repo/" } google() jcenter() mavenCentral() } }
這樣我們就可以在本地進行模擬發布和依賴 library 的功能。畢竟,倉庫地址只是個地址而已。
這樣在 library 依賴這個配置文件?apply from: file("publish.gradle")
?后,我們便可以在 gradle task 中看到發布任務了。
發布內容
通過雙擊圖中的?publish
?任務或者是命令行執行?gradlew :thirdlib:publish
?就可以開始執行發布任務,發布任務成功后我們就可以到?${rootDir}/local_repo/
?查看一下到底有哪些產物。
有哪些內容
發布產物看著很多,但其實只有這么幾類
- aar 文件
- module 文件
- pom 文件
- source.jar
其余都是這些文件的數字簽名。
用 Android Studio 看一下 aar 的內容。
可以看到 library 源碼中的 xxx.so 和 assets 目錄下的文件會按照編譯時的配置和內容,原封不動的打包到 aar 中。即便是沒有使用到的內容。
aar 的內容就是我們的代碼了,source.jar 是源代碼,方便依賴 library 的時候進行查看。那么剩下的 module 文件和 pom 文件有什么作用呢?我們結合剩下的問題來解決這個疑問。
依賴的規則
關于依賴,一般有兩種:
- 將 xxx.jar 或 xxx.aar 文件放到本地 libs 目錄下,然后通過?
implementation fileTree(include: ['*.jar','*.aar'], dir: 'libs')
?進行依賴。 - 通過?
implementation 'com.google.code.gson:gson:2.9.0'
?這種直接聲明中央倉庫?group_id:artifact_id:version
?的方式進行依賴。
這里的情況比較復雜,分開說明一下。首先看本地依賴直接依賴 libs 包的內容。
本地依賴
aar
直接從 libs 包依賴三方組件的時候,如果是 aar 包,那么為了編譯生成 library 自己的 aar ,則只能用?compileOnly fileTree(include: ['*.aar'], dir: 'libs')
?的方式。否則會報錯:
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.
至于為什么有這種限制官方的報錯信息已經說明原因了。顯而易見,本地依賴的 aar 包是無法打進最終產物的。?那么如果必須要用到這個 aar 該怎么辦呢?兩種辦法,一是將 aar 發布到中央倉庫,進行遠程依賴;二是哪里缺這個 aar ,就在哪里直接進行本地依賴。
jar
jar 類型的文件要分情況:
-
compilyOnly
?依賴,那么不會輸出到最終產物中。 -
implementation
?和?api?
依賴,將在最終 aar 文件的 libs 目錄下保留相應的依賴。
可以看到源碼中 libs 目錄下的兩個 .jar 文件最終都輸出到了 aar 文件中。這里需要注意的是,class.jar 中并沒有包含 libs 目錄下 jar 文件的?*.class
?文件,只是一些配置文件。
中央倉庫的依賴
這里首先需要明確一點是,只要是通過聲明中央倉庫?group_id:artifact_id:version
?的方式進行依賴,那么所依賴的內容一定不會打包到最終的 aar 文件的。這么做是合理的,也是必須得,否則這 Android 的依賴管理將是一場噩夢。試想一下,如果所有依賴的東西,都可以打到一個包里,那如果發生沖突了,豈不是要解到天荒地老。
通過聲明中央倉庫?group_id:artifact_id:version
?的方式進行依賴的內容,在編譯時必然是需要的,那么依賴這個 library 的使用方,又該如何獲取到這些依賴呢?這就要靠前面提到的 pom 文件了。
這里我們先比較一下,dependencies 各種配置一下,pom 文件的內容。
- 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
?這個在使用 Android Studio 2.x 版本時的常見的配置,是不是有種似曾相識的感覺。可以看到
- 本地依賴的配置不會出現在 pom 文件中
- compileOnly 依賴的內容,不會出現在 pom 文件中
- implementation 在 pom 文件中 scope 變成了 runtime
- api 在 pom 文件中 scope 變成了 compile
這些都是符合預期的。關于 pom 文件是什么以及 scope 的作用,這里就不解釋了,網上有很多相關的科普,很容易理解的。
當我們在 gradle 中配置一個三方庫依賴的時候,在 Android Studio 底部的信息欄你應該大概率看到過 download xxx.pom 文件的信息。gradle 就是通過這個 pom 文件的信息,明確當前依賴的 library 自身還依賴了那些內容,這就是所謂的依賴傳遞。
沖突
關于依賴,其實還有點其他內容。如果你是一個有追求的開發者,有代碼潔癖。一定希望自己輸出的東西越干凈越好,使用者用起來越簡單、問題越少越好。比如你聲明的依賴和使用者聲明的依賴沖突了怎么辦?人家用的 glide 就是版本就是 3.x ,okhttp 版本就是 3.10.x ,而且還不能隨意變動,你比人家高該怎么辦? 這時候對外輸出的時候就要做好選擇,到底是用?compileOnly
?還是?api
?。也可以使用 exclude 排除一些自身沒有用到的內容,既可以排除可能造成沖突的依賴,又可以稍微加快一定編譯速度,何樂而不為呢。
比如像下面這樣:
implementation("com.squareup.retrofit2:retrofit:2.9.0") { exclude group: "com.squareup.okhttp3", module: "okhttp" exclude module: "okio" }
本身只是使用 retrofit 進行了一些簡單的封裝,完全可以將 okhttp 相關的內容排除掉。盡可能避免給使用方帶來沖突的可能性。
<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 文件會很自然的記錄這種優化,使用方在依賴的時候,也會排除這些依賴,自然不會莫名其妙的引入一些意料之外又用不到的東西。
當然,這種沖突也不是致命的,使用者也可以自己配置 exclude 移除沖突的依賴項,或者是主動聲明關閉依賴傳遞。
module 文件的作用暫時沒理解出來,有知道的同學可以科普一下??囧
混淆配置
最后再說一下混淆配置。一般情況下,當我們新建一個 library 類型的 Android Module 時,會自動創建?proguard-rules.pro
?和?consumer-rules.pro
?這兩個文件。為什么需要兩個,各自有什么用呢?
其實也很簡單,通俗來說:
-
proguard-rules.pro
?是 library 自己用的 -
consumer-rules.pro
?是給依賴這個 library 的使用者用的。
proguard-rules.pro
?中配置當前 library 中那些內容需要混淆,那些內容需要保留。比如以?thridlib?library 為例。
分別對這兩個文件進行配置,這里簡單起見,隨便配置一下,主要是說明問題。
proguard-rules.pro
-keep class com.engineer.third.internal.NativeMethodsFactory {*;}
consumer-rules.pro
-keep class com.engineer.third.** {*;}
我們看一下打包輸出的 aar:
可以看到?proguard-rules.pro
?配置的內容對當前 library 生效了(當然這個配置完全可以不用寫,Android 自帶的 proguard 規則會 keep native 方法及類,這里只是舉例)
這個就比較有意思了?consumer-rules.pro
?的內容變成了?proguard.txt
?。那么這個文件,又有什么用呢?這里就牽扯到另一個知識點了,Android 在輸出 release apk 進行混淆的過程中,會收集每個依賴庫中的混淆配置,合并到一起。生成一個最終的混淆配置文件,最終會基于這個文件進行混淆處理。這個總的混淆配置文件一般在?app/build/outputs/mapping/falvor/configuration.txt
?中,其中就可以看到各個依賴庫的混淆說明。
這就是 library 中混淆配置的用法。
小結
本文通過總結發布 library 和平時使用三方 library 時的一些疑問和困惑,就普通情況進行了相關的梳理。這樣下次遇到相關問題時就有理可循了,不會再抓蝦
原文鏈接:https://juejin.cn/post/7144712912521658382
相關推薦
- 2022-10-05 教你使用RustDesk?搭建一個自己的遠程桌面中繼服務器_服務器其它
- 2022-10-05 Android開發Activity毛玻璃背景效果_Android
- 2022-04-22 Golang執行流程詳解,兩種執行流程方式有什么不同
- 2022-08-12 python關于字典及遍歷的常用方法_python
- 2022-09-06 關于react+antd樣式不生效問題的解決方式_React
- 2023-07-25 MyBatis數據操作和動態SQL
- 2022-09-14 C語言指針如何實現字符串逆序反轉_C 語言
- 2022-11-17 Python中的優先隊列(priority?queue)和堆(heap)_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支