查看依赖库和隐含依赖导致的问题

2016-09-07  本文已影响1661人  RoFF

Android Studio使用gradle来管理项目,使用依赖库也很简单,在build.gradle中配置一下就可以了。但是这么方便的工具也隐含很多坑,隐含依赖问题就是其中之一。

例如如果同一个jar包却有两个版本,导致ClassLoader加载出错,或者干脆生成dex的时候就报错,让你编译都不过。就是说这类错误可能发生在编译期,也可能发生在运行期(例如加载多个dex)。错误提示也是多种多样。

查看依赖库的命令行工具

$gradle <host_module_name>:dependencies 可以看到指定module的所有的间接依赖库,也就是依赖库的依赖库

$gradle showAllCache 可以看到所在module的所有依赖库的路径

前提是在module的build.gradle中加入showAllCache的task,如下

task showAllCache << {
   configurations.compile.each { println it }
}

遇到版本依赖问题总是很烦,需要仔细看log,有了查看依赖的方法就能省很多时间。

怎么定位是依赖库的问题?

错误提示多种多样,没有统一的log。如果是没见过的错误,可以google一下,一般都会有人遇到,然后说这是依赖库的版本冲突或干脆就说是依赖库的问题,但是也就到这里为止了(可能因为错误一般都是自己项目特有的,不好写总结)。

下边举两个例子,都是我遇到的。

错误举例

1 隐含依赖导致混淆出错 :app:transformClassesWithMultidexlistForDebug

在打包过程中报错,详细的log见我在stackoverflow上的问题

看log里有提示

“Error:Uncaught translation error: com.android.dex.util.ExceptionWithContext: name already added: string{"a"}”

这是我在注音输入法里遇到的,build.gradle中加上使用

minifyEnabled = false

打包成功;

minifyEnabled = true

打包失败。很显然跟混淆有关。

问题是怎么定位到具体是哪个库没做混淆或者不应该混淆呢?
build.gradle中用到的所有库我都在proguard中加了keep,为啥还有问题?

我当时用了笨办法,一个个排除。结果花了很长时间才找到出问题的依赖库。根本原因就是com.squareup.retrofit2依赖fastxml,后者没在proguard中被keep

后来在网上找到上边查看间接依赖的方法,试了一下,果然很好使。早知道这个方法的话我就能省很多时间了:-(

2 隐含依赖导致版本冲突
W/art: Incompatible structural change detected: Structural change of android.support.v4.app.FragmentActivity is hazardous (/data/user/0/<package_name>/files/dexOpt/com.tugele.expression.odex/plugin_doutu-debug.dex at compile time, /data/app/<package_name>-1/oat/arm/base.odex at runtime): Virtual method count off: 45 vs 46
W/art: Landroid/support/v4/app/FragmentActivity; (Compile time):

这个错误是在通过输入法启动图个乐插件(单独的apk包)时发生的。排除各种错误后把问题定位到android.support.v4的版本上,host和plugin使用了不同版本的support-v4版本上。

分析过程

第一步:验证一下错误原因是否属实。分别把host(主工程)和plugin(插件)的dex反编译成jar,然后用jd-gui查看FragmentActivity.class,发现确实不一样。

那问题就来了,host和plugin中指定的android-support-v4.jar的版本是一样的,build.gradle中写的都是“compile 'com.android.support:support-v4:23.1.1'”,版本一样为啥看到的代码不一样呢?

第二步:验证一下项目实际依赖的库。

怎么验证?

到host和plugin目录下,分别在命令行下执行 $gradle
host的依赖输出:

...................
<SDK_PATH>/extras/android/m2repository/com/android/support/support-v4/23.2.1/support-v4-23.2.1.aar
....................

plugin的依赖输出:

...................
<SDK_PATH>/extras/android/m2repository/com/android/support/support-v4/23.1.1/support-v4-23.1.1.aar
....................

发现问题木有?
host和plugin实际依赖的版本不一样!!!

为什么会这样?host为什么不用指定的版本23.1.1 ?

原因就是其它依赖库间接的依赖android-support-v4-23.2.1。那么是谁间接依赖它呢?

要查明原因就要用这个命令:$gradle <host_module_name>:dependencies
它能把所有库的依赖关系都展示给你。

看我的结果:

+--- project :zhushou_sdk
| +--- com.android.support:recyclerview-v7:23.2.1
| | +--- com.android.support:support-v4:23.2.1 (*)
| | --- com.android.support:support-annotations:23.2.1

看到了吗?com.android.support:recyclerview间接依赖到com.android.support:support-v4:23.2.1

再多看一点:

+--- project :explore_base_module
| --- com.android.support:support-v4:23.1.1 -> 23.2.1 (*)

这个module本来是依赖23.1.1的,gradle要照顾到版本兼容问题,所以就该成了23.2.1

这就是问题的根本原因。

上一篇下一篇

猜你喜欢

热点阅读