Proguard剥离工具接口。在运行时导致java.lang.IncompatibleClassChangeError

时间:2018-07-17 12:32:17

标签: android android-gradle proguard dagger-2 android-proguard

将依赖关系更新到最新版本后,由于缺少接口实现,我们在发行版本中遇到了运行时崩溃

java.lang.IncompatibleClassChangeError: 
Class 'com.mypackage.app.data.cache.query.user.QueryUserFollowersCountById' 
does not implement interface 'com.mypackage.app.data.cache.query.Query' 
in call to 'java.lang.String[] com.mypackage.app.data.cache.query.Query.c()' 
(declaration of 'com.mypackage.app.data.cache.database.util.Db' 
appears in /data/app/com.mypackage.app-2/base.apk:classes2.dex)

经过两天的调试,我们认为问题与Proguard在收缩阶段剥离“实现查询”有关。该接口本身保留不变,因为它在数百个其他类中使用,但仅在3个类中丢失。我们还发现在某些地方以相同的方式剥离了一些RxJava Func0和Action0接口。因此,该应用程序可以在95%的屏幕上完美运行,但显然会在缺少界面实现的地方崩溃。

  1. 我们正在使用Gradle 3.1.3版本的构建工具,但也尝试使用3.3.0-alpha03,还尝试禁用了D8,因此它不应该是构建工具的问题(Proguard 5.3.3和6.0.3的行为也一样)在这种情况下也是如此)
  2. 将Dagger2从2.11版本升级到2.12或更高版本时出现问题,因此它可能与应用程序中的字段/方法/类等的数量有关
  3. 即使使用-dontoptimize,问题仍然存在,我们已经阅读了Proguard文档并启用/禁用了几乎所有相关标志
  4. 通过将minifyEnabled设置为false或对Proguard使用-dontshrink标志可以解决此问题
  5. 该应用程序使用Multidex,两者均使用Dagger 2.11和Dagger 2.12构建,最终都带有3个classes.dex文件。在两种情况下,有问题的界面和实现都在同一个.dex文件中。
  6. 例如,同一程序包中有5个文件实现Query,其中3个文件在结果字节码中具有接口,而2个文件则没有。因此,它不应与文件放置位置有关。

使用Dagger 2.11或-dontshrink构建的字节码

.class public Lcom/mypackage/app/data/cache/query/user/QueryUserFollowersCountById;
.super Ljava/lang/Object;
.source "SourceFile"

# interfaces
.implements Lcom/mypackage/app/data/cache/query/Query;

# annotations
.annotation system Ldalvik/annotation/Signature;
    value = {
        "Ljava/lang/Object;",
        "Lcom/mypackage/app/data/cache/query/Query<",
        "Ljava/lang/Integer;",
        ">;"
    }
.end annotation
......

使用Dagger 2.12或更高版本构建时的字节码

.class public Lcom/mypackage/app/data/cache/query/user/QueryUserFollowersCountById;
.super Ljava/lang/Object;
.source "SourceFile"

# annotations
.annotation system Ldalvik/annotation/Signature;
    value = {
        "Ljava/lang/Object;"
    }
.end annotation
......

我们显然希望使用Dagger2的最新版本,并希望通过Proguard继续缩小和优化我们的代码。

  1. 如何确保Proguard不会剥离实现接口声明?
  2. 或者如何在Proguard缩小步骤中添加日志记录/调试功能?

1 个答案:

答案 0 :(得分:0)

如果proguard删除了工具部分,则在优化阶段,proguard更有可能将其视为无效代码。

您已经提到

  

通过将minifyEnabled设置为false或对Proguard使用-dontshrink标志可以解决此问题

这证实了我的假设,即实现部分可能是无效代码

请仔细阅读此thread,这说明了如何获取无效代码列表。换句话说,有关使用-printusage [filename]

添加调试日志的查询的答案