Moyudawang
V2EX  ›  Android

Android 项目多个 Jar 包有命名空间冲突,有办法解决吗?

By Moyudawang at 2021 年 4 月 21 日 · 15823 次点击

简单来说,同一个芯片的 SDK 被两个硬件组装厂家做了封装,我们的项目添加两个 Module 分别引用两个厂家封装后的 SDK(jar 、so),然后他们引用的芯片 SDK 就出现命名空间冲突了,而且他们用的芯片 SDK 版本还不一样,让厂家修改也不现实。
现在就一直报错几个 Module 引用的 Jar 文件中有相同的内容,
尝试把 Module 编译成 dex 动态加载,可是 dex 不能把 jar 和 so 打包进去,
有没有大佬遇到过这种问题?望赐教

38 条回复  •  2021-04-22 11:59:39 +08:00
HongJay
   1
HongJay  
   2021 年 4 月 21 日
jar 不能编辑么。gradle 不是有排除语法么
Moyudawang
   2
Moyudawang  
OP
   2021 年 4 月 21 日
@HongJay 厂家的 SDK,不能编辑,因为用的版本不同,也不能排除
xne110
   3
xne110  
   2021 年 4 月 21 日
如果就是个 Jar 的话,我倒是遇到过,我是直接用 zip 打开 Jar 把里面重复的东西直接删了 ,在导入就 ok 了
Moyudawang
   4
Moyudawang  
OP
   2021 年 4 月 21 日
@xne110 是硬件厂家引用了芯片厂家的 so 和 Jar,我们再引用硬件厂家的 Jar
Moyudawang
   5
Moyudawang  
OP
   2021 年 4 月 21 日
@xne110 而且他们用的芯片 Sdk 版本也不一样,直接删除不行
wsxyeah
   6
wsxyeah  
   2021 年 4 月 21 日 via iPhone
改 bytecode,参考下 Android X 的做法
cache
   7
cache  
   2021 年 4 月 21 日
我记得有个工具专门干这个事
好像叫 jarjar
xne110
   8
xne110  
   2021 年 4 月 21 日
就是有两三方包里引用了 同一个不同版本的三方包 两个直接删了,在自己项目里引用那个相同三方包 不就好了,如果他们混淆了 那就没办法了 ,我可能想的比较简单哈
Moyudawang
   9
Moyudawang  
OP
   2021 年 4 月 21 日
@wsxyeah 看一眼头大,慢慢研究一下
@cache 我去试试
@xne110 不同版本不一定向下兼容,命名一样,实现不一定一样
KotlinAmai
   10
KotlinAmai  
   2021 年 4 月 21 日
你们是多种设备吗,每种设备使用不同的 SDK 。如果是这样的话,可以考虑多维度打包。每种设备只打入自己需要的包。关键字:flavorDimensions,productFlavors 。这样每种设备打出来的包是不一样的。缺点就是每一种设备都会打一个包,后续更新会需要维护多种设备的安装包。但是打包的时候会将多种设备的安装包同时打出来,不需要一个一个的打包。
Moyudawang
   11
Moyudawang  
OP
   2021 年 4 月 21 日
@actar 是的,适配多种设备,现用的也是这种办法,不过还是想只打包到一个 App 里,这样发布到商店也简单点
wms
   12
wms  
   2021 年 4 月 21 日
把两个 SDK 用到的部分做成两个独立的服务, 主 APP 去跟这个两个服务通信
Moyudawang
   13
Moyudawang  
OP
   2021 年 4 月 21 日
@wms 那这两个服务不也是 App 启动的吗?现在编译通不过
wms
   14
wms  
   2021 年 4 月 21 日
@Moyudawang 每个服务做一个 APK, 将三个 APK 打包成一个
timethinker
   15
timethinker  
   2021 年 4 月 21 日
自己实现一个类加载器,加载不同 SDK 的 jar 可行性如何?
就是说把厂商 SDK 的 jar 当做文件资源,不直接通过 gradle 打包,而是使用动态加载的方式载入需要的依赖。
lawfun
   16
lawfun  
   2021 年 4 月 21 日
我觉得 @qwe520liao 说的可行。

原文中的
“尝试把 Module 编译成 dex 动态加载,可是 dex 不能把 jar 和 so 打包进去”
可以把 so 都打进 主 APP 里。

破解 jar 看源码,分析代码自己再实现下应该也行,不过可能会很费时。
Moyudawang
   17
Moyudawang  
OP
   2021 年 4 月 21 日
@wms 我还没试过这种,可以尝试一下
@qwe520liao android 不能动态加载 jar 吧,只能先编译成 dex,第三方的 jar 我试了,编译成 dex 失败
@lawfun 这个确实太费时了,因为厂家的 jar 包不止包含芯片的功能,还有厂家其他硬件的功能
Moyudawang
   18
Moyudawang  
OP
   2021 年 4 月 21 日
@cache 试了不行,Jar 里含有 Jni 方式加载 So 里的方法,所以命名空间不能改,
mmrx
   19
mmrx  
   2021 年 4 月 21 日
@Moyudawang #14 的方案感觉可行,俩库的 apk 当做服务,用哪个装哪个 apk,然后用 AIDL 通信
Moyudawang
   20
Moyudawang  
OP
   2021 年 4 月 21 日
@mmrx 嗯,这个方式我需要花时间尝试一下
kingiis
   21
kingiis  
   2021 年 4 月 21 日
看你说涉及到了 so 文件
只能从源码层面 二次开发重新导出
ParfoisMeng
   22
ParfoisMeng  
   2021 年 4 月 21 日   ❤️ 1
为啥让厂家修改不现实……
p2pCoder
   23
p2pCoder  
   2021 年 4 月 21 日
maven 有 maven-shade,gradle 应该也有类似的工具
Moyudawang
   24
Moyudawang  
OP
   2021 年 4 月 21 日
@kingiis 没有源码,也没有这个能力
@ParfoisMeng 因为让他们改过,有的改过也不行,有的说改不了,例如设备上的扫描头模块,很多组装厂家用的是一样的模块,但是为了连接他们的硬件又把扫描头的 sdk 封装了一层,要改大家全都一起改,太难了
@p2pCoder 这还真不知道
Moyudawang
   25
Moyudawang  
OP
   2021 年 4 月 21 日
@ParfoisMeng 也怪我们采购了太多种类的设备,厂家提供的 sdk 都是大一统集成在一起的(RFID 、NFC 、条码扫描头),用起来是方便,冲突了就麻烦了
xFrye
   26
xFrye  
   2021 年 4 月 21 日
没有包名检测的话,用 jarjar 就好,之前接一个第三方库也有个冲突的,给他命名空间改了
Moyudawang
   27
Moyudawang  
OP
   2021 年 4 月 21 日
@xFrye 有些方法是用 JNI 调用的,包名不能改
2bab
   28
2bab  
   2021 年 4 月 21 日
1. 编译期解决的话,改命名空间最快
2. 运行期解决的话,我依旧推荐动态加载 + 区分 Classloader,打不出 Dex 多半只是姿势问题,懒得研究的话,你可以直接建一个全空的 Android 工程,就引用这个 SDK,然后打了 APK 再把 Dex 给解压出来
Moyudawang
   29
Moyudawang  
OP
   2021 年 4 月 21 日
@2bab 嗯,我也觉得动态加载才是根本解决办法,我再研究研究 dex,多谢提醒
ParfoisMeng
   30
ParfoisMeng  
   2021 年 4 月 21 日
@Moyudawang 如果是版本不统一属实难搞,可以和每个厂家约定统一使用最新稳定版本。然后都打 without 包( compileOnly )出来,你自己去依赖指定版本。
littlewing
   32
littlewing  
   2021 年 4 月 21 日
@xne110 并不是,而是两个同名的包是不同功能,而且两个都需要用到
limbo0
   33
limbo0  
   2021 年 4 月 22 日
maven-shade 正解, 把 A 厂家的包 shade 成 a.com.x.x , B 厂家的包 shade 成 b.com.x.x, build 完后引入你的项目, 互不影响
Moyudawang
   34
Moyudawang  
OP
   2021 年 4 月 22 日
@ParfoisMeng 有的设备都是几年前的了,厂家懒得搞

@humpy 感谢指点
@limbo0 谢谢 我会去试试
xyooyx
   35
xyooyx  
   2021 年 4 月 22 日 via iPhone
独立类加载器加载
Aviciii
   36
Aviciii  
   2021 年 4 月 22 日
我遇到过项目需要引用不同版本的 jar 包,最后用的 jarjar.jar 改的包名,然后把相关引用也改掉。不知道对你有没有帮助。
ntdll
   37
ntdll  
   2021 年 4 月 22 日
假设 a.jar 包含 com.sample.MyClass,b.jar 包含 com.sample.MyClass,
a 与 b 的功能完全不同,则可以考虑在编译时,不引入 a.jar 与 b.jar

在使用期间(运行期间)通过不同的 ClassLoader 分别加载 a 与 b 即可
对应到 Android 即 BaseDexClassLoader 。

缺点是需要通过反射调用 a 与 b 的方法,当然如果他们有共同实现的接口或共同的父类,可以把这个这个接口或类放到主 jar 中,可以方便调用。
Wounmay9976
   38
Wounmay9976  
   2021 年 4 月 22 日 via Android
感觉#14 是开发最优,要是能接受这样最简单快捷。
© 2026 V2EX · 84ms · 3.9.8.5