一、前言
在开发 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 ,总是有风险的,可能会造成不可预料的问题,最好还是尝试和第三方沟通,说明情况,由第三方来提供一个修改后的包进行集成。
【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】