android studio 项目编译,Jar包重复引用。
还是说下故事背景,公司有个产品,前前后后已经有三个版本了,是基于Eclipse的项目工程结构,随着需求变更、产品发展,整个项目也变得非常庞大,当时有五六个library工程,工程引用的第三方Jar也就不说了,一二十个是有的。由于最初这个产品是基于Xamarin开发的,是使用C#作为基础语言,所以各种原因导致项目使用了大写包名,其实大写包名在Eclipse上未出现什么爆炸性的问题,但是等到有一天我想把这个项目移植到AndroidStudio上,就出现了一个致命性的问题,android studio不支持大写包名,因此只能放弃,至少在我写博客这个时间点,android studio是无法支持大写包名的,能编译成功,但不能运行在设备上。当然这些都是体外话,并不重要。不过提醒各位一点的是,记得哈,Android Studio不支持大写包名(呵呵,这个很只有少数奇葩情况有人会这么搞吧)。
问题来了,有一天公司新开业务线,也是需要做产品,由于之前那个工程功能还是比较强大,因此想基于现有工程开发出一个产品,哈哈,多么好的一个时机,这样就可以把之前的项目更改包名移植到android stduio上了,问题也就随之而来了,让我编译了两天才把工程编译成功,我的妈,眼睛都差点跟我看瞎了,下面说下我遇到的问题。
1、App工程无法使用本地aar的远程依赖对象
之前我不是说公司有五六个library工程嘛,由于项目太多,而且大多是一些工具库,封装的一些东西,通常都不会怎么去更改,于是我就想把他们到成jar包文件(带有资源的就打包成aar),打包这个工程就没啥说的了,然后我把打包aar给工程引用,发现aar里面远程依赖的一些第三方库里面的资源无法访问,举例说明,比如我有个LibraryA工程,打包成了a.aar,这个远程依赖了Retrofit,然后有个管理器类返回一个Retrofit对象,但是我发现app根本就不知道Retrofit的存在,也就是说app工程根本就无法访问Retrofit这个对象。几经周折,我偶然发现a.aar中以jar包形式引用的资源是可以被访问到的,因此我就把所有的远程依赖都换成了放在libs目录中的jar或者aar文件,然后进行打包,这个时候工程再引用,就能够访问libraryA工程所依赖的资源了。
但是说实话,这种解决方式实在恶心,而且也对后续工程引用jar上增加了限制,以及jar升级之类的,这些都是问题。后来我再网上各种查找资料,找到一篇文章,当然是另一种方式解决,不过这种方式一样还是存在前面说的依赖升级的恶心问题,不过这都是小问题,需要升级的话,升级之后再打包编译即可,也费不了多少事情。
另外一种解决方式见: 别人的方式确实要比我找到的方式高级很多,而且别人还图文并茂,我败了。
2、Jar包的重复引用
当你遇到类似 "transformClassesWithJarMergingForDebug" "duplicate entry"等关键字的时候,恭喜你,几乎可以肯定出现jar包的重复引用了。
如果在比较简单的工程关系中,这个问题其实还是相对好解决一些,毕竟工程结构清晰,不至于让人眼花缭乱,把人绕晕,但是也会出现比较难找的情况,下面我就说下我在转换工程是遇到的一些情况。
1)、不同版本jar包引用:最简单的也最容易找到的,也就是不同项目引用了同一个jar包的不同版本,比如一个引用okhttp 1.0,一个引用okhttp2.0,这种情况肯定是要删除一个的,连Eclipse都不会让你编译通过。那你可能就要我,我删掉jar包的工程怎么办呢?删掉了他就没有这个jar包引用了啊,这里有两种情况,第一种比如你A和B两个项目各自引用了两个不同版本的jar包,并且B依赖A项目,这个时候,让A去依赖Jar包,B删除Jar包,其他不用处理,因为这就好比引用传递,你依赖了我就不用依赖了。
2)、远程依赖的与本地jar包版本不同:这种就复杂一些了,比如我工程中本身依赖了Okhttp-2.4版本的jar包,主要解决文件上传下载,转到android stduio上我想换成Retrofit作为网络请求,这个时候运行的时候就出现重复引用了,因为retrofit2.0默认引用okhttp3.0,所以这种情况你就的知道你的远程依赖的一些信息,那样就更利于你解决问题(当然有log会提示什么重复定义了,这个时候根据提示慢慢解决)。
3)、同版本jar包多副本:这是啥意思,这个就是说同一个文件,你放在A和B的lib目录中,并且各自引用,这种情况也会出现问题。不过顺带提一句,这种情况在eclipse上并不会有问题,也就是说,eclipse中,你把同一个jar包文件复制多份放在不同的工程中,编译运行不会有问题,但是Android stduio不行。这个怎么解决呢,当然前面1)问题也提供了一些解决办法,必然是要清理掉只剩一个,然后懵逼了,因为可能有些项目不存在引用关系,也就是A和B项目不存在依赖关系,但是由于只保留一个jar文件,必然有一个没有,这个时候有两种方法,当然原理是一样,就是删除其中一个,让另一个项目引用另外一个项目里面的文件,这个具体怎么写呢,我也懒得写代码,也就一句话,因为这也不是常规做法,常规做法是把工程中引用的jar都放到一个公共目录中,通常可以选择是project根目录下的libs目录,然后让其他的工程都引用这里面的,这样可以避免同个jar多个副本文件的情况。
4)、貌似就遇到上面些情况。。。
3、工程超过65536方法数限制
这个在我把工程的网络框架增加retrofit时出现了这个问题,这个问题的解决办法,网上一大堆,也好容易解决。
不过这里有个注意的地方:如果你的工程包含多个Module,那么每个Module都必须支持在工程的build文件中增加 multiDexEnabled true,否则你可能在编译中遇到xx无法merge等异常。大致如下配置
defaultConfig {
...
// Enabling multidex support.
multiDexEnabled true
}
3、Library Module工程无法使用自己工程的资源文件异常
这个问题着实让我懵逼了好久,一直到现在,就是一个Library Module工程有一些自定义控件,并且使用了项目本身包含的一些图片资源。编译通过了,也正常在手机上运行了,可能当使用到这个自定义控件的地方,就会出现崩溃,经调试在使用R.drawable.xxx的地方出现了NoClassDefFoundError,真的,我懵逼了,超级懵逼,连续两天一直被这个问题折磨,因为我还有个工程,项目结构跟这个极其相似,但是他运行却没有问题。
之前我一个同学,说让我尝试提高gradle的版本,尼玛说实话,android stduio项目管理方面的,我还真不熟,后来在他的指点下,我更换了版本,从2.4更换2.10,说实话,更换之后连编译都不通过了,但是我隐约觉得有救了,为啥呢,在2.4上至少能编译运行,现在连编译都不通过了,我怎么还能觉得有救呢,直觉,后来我也看了那些错误信息,都是一些jar包重复引用的问题,咦,奇了怪了,前面不是解决了一些重复引用问题吗,怎么这会儿有蹦出这样的问题来呢,这尼玛什么鬼,我仔细一看,跟之前的并不一样,OK,有问题解决问题,跟着前面的步骤,再次解决,就在我还在写文章记录的时候(因为都是项目编译的问题,大家都知道,反复的同步项目,clean,rebuild这些操作,是相当耗时的,需要等待很多时间,因此我就觉得应该写点什么记录下之前遇到的问题),就在这个时候,一切问题解决完毕,编译,运行,尼玛,问题没了,我的个天,everything is ok。看来今晚可以安心吃顿肉了。
到此一个庞大结构的项目从eclipse转移到android studio中正常编译运行,呼呼。。
PS:关于这个问题,经过前面那一番波折,项目最终正常运行了两天之后,又莫名其妙的出现同样的异常,又是一番折腾,这次是处理了项目对fastjson上的依赖,之后又能正常运行了,不过在另外的界面又出现了同样的异常,但是这次不是资源id引用引起的,又是一番折腾,由于我平时工作用的台式机,因此在台式机上折腾的时候,我尝试在自己的笔记本上,更新了android studio,gradle,然后再笔记本上运行,莫名奇妙的就没问题了,所以总体来讲,保持IDE &gradle的更新,也许就能够避免这种没技术含量却又异常折磨人的问题。
等后面有时间了在贴上详细的问题以及过程。