版權聲明:
本賬號發布文章均來自公眾號,承香墨影(cxmyDev),版權歸承香墨影所有。
未經允許,不得轉載。
一、前言
在開發 App 的時候,經常會有需要借助第三方 SDK 的情況。但是有時候多方提供的 SDK 中,可能引入了同樣的庫,或者類的名稱以及包名完全一樣的情況。這樣的話,如果同時對這兩個 SDK 進行引入的話,就會出現 duplicate entry 的錯誤。
不談什么和對方協商,給出一個符合我們使用要求的包這種事,只是從技術的角度來看,如何解決這個問題。
二、分析問題
首先,分析問題。如果對方的 SDK 是使用 Gradle compile 的方式引入的,并且對方集成其他庫的方式也是如此的話,可以在 Compile 中配置 exclude 的方式剔除掉引入的庫,這是一個理想化的做法。關于 exclude 的使用,之后有機會再講,這不是本文的重點。
使用 exclude 是一個理想的情況,多數情況下,duplicate entry 的沖突,都是來自對方的代碼中的類(可能對方使用引入源碼的方式引入的開源庫),這種情況下,使用 exclude 就不好使了。
那么既然是 Java 類重復了,那么如果我們有辦法去修改某一個 SDK 中,類的包名,就可以解決這種問題了。
接下來就是我們修改 jar 中類的包名的工具上場了:jarjar.jar。
二、jarjar.jar
1、什么是 Jar jar
Jar Jar Link 是一個實用的工具,它可以輕松的重新打包 Java 庫,得到一個沒有外部依賴的單獨 jar 包,從而很好的嵌入到我們發布的項目內。而我們這里,使用 jarjar.jar 的主要作用,就是為了解決 duplicate entry 這種文件沖突的情況。
jarjar 提供了非常方便的 *.jar 工具來供我們使用。這是一個開源的項目,同時也提供了和 Gradle 配合使用的方式。通常這種操作,我們并不是很常用,所以一般在需要使用的時候,做一次修改就可以了,沒必要集成到項目中。
JarJar 的地址:https://code.google.com/archive/p/jarjar/
配合 Gradle 的使用,項目的 readme 已經寫的很清楚了,有興趣的可以去看看。
https://github.com/shevek/jarjar
2、使用 jarjar
既然多數情況下,我們不需要頻繁的修改 jar 包,所以這里只是提供如何使用 jarjar.jar 這個工具來幫我們對 jar 包進行修改。
這里使用當前能下載的最新版:jarjar-1.4.jar
下載地址:https://code.google.com/archive/p/jarjar/downloads
開始使用前,閱讀一下幫助文檔是有必要的,除了可以 github 上閱讀到使用文檔之外,還可以通過命令的方式查看 jarjar.jar 的使用文檔。
java -jar jarjar.jar
文檔很長,就不在這里截圖展示了。
jarjar.jar 從文檔上看,jarjar.jar 的核心命令就三個:
- 查看幫助:
jarjar.jar
- 查看所有包名:
jarjar.jar strings <xxx.jar>
- 更換包名:
jarjar.jar process <rulesFile> <inJar> <outJar>
Jarjar 雖然提供了查看包名的方法,但是一般也不怎么使用它,這里簡單舉個例子,提供一個 cxmylib.jar 的包,先使用 strings
命令看看它的內容吧。
可以看到,cxmylib.jar 內部其實非常的簡單,如果復雜的 lib 的話,會將所有的包全部輸出出來。
使用 jarjar 最重要的方法,還是用來修改 Jar 的命令:
java -jar process <rulesFile> <inJar> <outJar>
inJar、outJar 非常的好理解,既然是修改 Jar 包,一個是待修改的 Jar 包,另外一個是修改之后重新輸出的 Jar 包。
但是這樣的一個修改,jarjar 如何知道是需要將哪些 packages 進行修改了,這個就需要使用 rulesFile 來進行規則的配置了。
3、rulesFile 配置修改規則
rulesFile 只要是一個文本文件就可以了,它主要包含三條命令。
1、rule 指定替換的 Package。
rule pattern result
2、zap 移除符合規則的 Package
zap pattern
3、keep 保留符合要求的 Package
keep pattern
其中 pattern 用來指定一個帶操作的 package ,為了方便操作,可以使用 「 *」、「 ** 」通配符的方式,來進行匹配,「 *」表示一個包名, 「 ** 」將匹配任何有效的類名稱的字符串 。而 result 可以指定 pattern 中通配符匹配的子字符串,通過 @1 ,@2 的方式來匹配。
這都是通配符的標準用法,沒什么好細說的。接下來看個例子就清楚了。
rule com.cxmydev.** com.cxmylibdev.@1
這樣的一條 rule 規則,就會將一個 com.cxmydev.a.java 替換成 com.cxmylibdev.a.jar 。
而既然有三個規則,他們必定是有優先級的。首先 zap 指定需要刪除的所有類,然后在執行 rule 規則替換符合要求的類,最后如果配置了 keep 規則的話,會再執行 keep 規則,將不符合規則的所有類的移除,只保留 keep指定的包。
總結來說,這三條命令的執行優先級是 : zap > rule > keep 。
4、舉個例子
首先,編輯 rule.txt 文件,來指定修改規則。
rule com.cxmydev.** com.cxmylibdev.@1
然后使用 process 命令,來進行修改。
java -jar jarjar.jar process rule.txt cxmylib.jar cxmylib_new.jar
就可以在當前目錄下看到修改后的 cxmylib_new.jar 文件了。
然后使用 jadx 看看源碼,驗證修改結果。
可以看到,已經修改成功了。
三、修改 aar
在 Android 項目中,可以被引入的庫,除了 jar 格式的,還有 aar 格式的。aar 和 jar 相比,簡單來說就是 aar 會多出一些額外的資源文件,例如:布局、圖片、顏色、so 庫 等。
那么我們碰到需要修改 aar 的情況,怎么辦呢?其實 aar 也是一個標準的壓縮包,所以我們只需要對其進行解壓,就可以得到 classes.jar 文件,對其進行修改再打包回去即可。
這里就剛才同樣的庫,打包出來的 aar 文件,進行修改。
1、使用 unzip 命令進行解壓
unzpi 解壓完成之后,就可以在 tmpDir 目錄下,看到解壓后的文件,其中 classes.jar 文件就是我們需要修改的 jar 包。
2、修改 classes.jar 文件
和前面舉例一樣,修改 classes.jar 文件之后,再替換掉它。
3、再使用 jar 命令重新打包回 aar
4、驗證修改后的效果
四、缺點
可以看到,如果只是需要修改一個現成的 jar 的包名并重新打包,使用 jarjar.jar 是非常的方便的。
但是它也是有缺陷的:
- 無法支持反射。如果在 jar 包內有使用反射調用的情況,是無法一并修改的。
- aar 的資源文件,也無法修改(jarjar.jar 只能修改 *.jar 文件)。
不管如何,自行修改第三方的 SDK ,總是有風險的,可能會造成不可預料的問題,最好還是嘗試和第三方溝通,說明情況,由第三方來提供一個修改后的包進行集成。
本文內的示例文件,可以關注 承香墨影(cxmydev),回復關鍵字:『jarjar』 來獲得。