【问题标题】:Proguard stripping out implements interface. Causing java.lang.IncompatibleClassChangeError at runtimeProguard 剥离实现接口。在运行时导致 java.lang.IncompatibleClassChangeError
【发布时间】:2018-07-17 12:32:16
【问题描述】:

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

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 不会剥离 implements 接口语句?
  2. 或者如何在 Proguard 收缩步骤中添加日志记录/调试?

【问题讨论】:

  • 嘿,你找到解决办法了吗?我也在为同样的问题苦苦挣扎……
  • 嘿@definera,很遗憾没有。听到这个问题直到现在 R8 和更新版本的 Dagger 2 都存在,我感到非常难过。

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


【解决方案1】:

如果 proguard 正在删除 implements 部分,则在优化阶段 proguard 更有可能将其视为死代码。

你已经提到了

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

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

请通过这个thread,它解释了如何获取死代码列表。换句话说,关于使用 -printusage [filename] 添加日志以进行调试的查询的答案

【讨论】:

  • 谢谢,我们查看了 usages.txt 文件,但类没有显示在那里(它们也最终出现在 dex 文件中)。由于缺少接口实现在运行时崩溃,我看不出它怎么可能是死代码。
  • 当我在考虑其他可能性时,您可以尝试使用 -dontoptimize 优化选项
  • 如 3 所述。我们已经尝试过了。关闭优化也是我们最“明显的解决方案”,但出人意料地没有帮助。这也是我们假设剥离发生在收缩阶段的原因。
  • 想不到更多了,你能不能也读一下guardsquare.com/en/products/proguard/manual/…上的“班级或班级成员不被保留”@这是我能想到的。可能会有所帮助
猜你喜欢
  • 2013-04-10
  • 1970-01-01
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-26
  • 1970-01-01
相关资源
最近更新 更多