ProGuard是一个压缩、优化和混淆Java字节码的工具,非常好用。本篇文章总结一下许多人在使用ProGuard时经常遇到的问题。

我把在使用ProGuard时经常遇到的问题分为两类,分别是导致构建失败的编译时问题,以及构建通过但运行时崩溃或结果不正确的运行时问题。大多数人所遇到的大多数问题,都可以在下面的内容中找到对应的解决套路。

在开始讲这两类问题前,先明确一点:我们所说的添加混淆规则,不是指加入了才会混淆相关的类,相反,事实上,当你启用混淆之后,添加的一些诸如-keep xxxx的规则才是起着不混淆的作用。

下面开始讲这两类问题。

编译时问题

问题

首先讲编译时的问题。导致编译不通过,最常见的情况是这样的:

在漫长的编译之后,我们等到的控制台上的这样一个输出结果:

...
Note: there were 8 references to unknown classes.
You should check your configuration for typos.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass)
Note: there were 272 unkept descriptor classes in kept class members.
You should consider explicitly keeping the mentioned classes
(using '-keep').
(http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass)
Note: there were 75 unresolved dynamic references to classes or interfaces.
You should check if you need to specify additional program jars.
(http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass)
Warning: there were 11 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)
Warning: Exception while processing task java.io.IOException: Please correct the above warnings first.

原因及解决方法

有些小伙伴会自动忽略英文日志,即便它给出了明确的答案,这是个严重的不良习惯。如上,其实在这段日志中,已经表明了原因及解决方案了。注意Warning开头的警告内容,最后一个警告是让你先解决第一个警告的内容,所以先忽略。我们看它前面的警告:

there were 11 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.

这是什么意思呢?

第一句话是告诉你原因:有11个未解析的类或引用

后面两句是解决方案。方案一:你可能需要添加丢失的库或更新它们的版本。方案二:如果你现在代码运行得好好的,也就是没有它们也没关系,那你可以使用-dontwarn来禁止这样的警告。

那么如何知道有哪些未解析的类或引用呢?

遇到这样的问题,很简单,我们往上翻,最终肯定会找到Warning开头的日志内容:

Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.OpenSSLProvider
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.OpenSSLProvider
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt

它的句型是这样的:Warning: xxxx.xxx.ABC: can't find referenced class xxx.xxx.XYZ

意思就是第三方库里的ABC引用了XYZ这个类,但是我在类路径中找不到XYZ这个类。

这种情况很常见,比如一个Java库使用了另一个库的一些类来实现某个特性。如果你在使用这个库的时候需要这个特性,那么你就要把另一个库也加进来,也就是前面给出的方案一。而如果你不需要用到,如上面的例子,项目中实际上用不到org.conscrypt包里的内容,那么我们在混淆规则的文件中添加上-dontwarn规则就可以了。

如何添加?

规则很简单。

-dontwarn 类名

在上面的日志中,有引用的类,也有被引用的类,这两者都可以。也就是对于前面的句型,你既可以使用-dontwarn xxxx.xxx.ABC,也可以使用-dontwarn xxx.xxx.XYZ

比如上面的例子,我们用

-dontwarn okhttp3.internal.platform.ConscryptPlatform

和用

-dontwarn org.conscrypt.OpenSSLProvider
-dontwarn org.conscrypt.Conscrypt

都可以解决问题。但从这里也可以看出疑惑来了。如果有多个类呢?是不是每一个类都要写上去呢?

当然不是。这里的类名是支持通配符的,比如上面,我们可以写为:

-dontwarn org.conscrypt.*

表示禁止org.conscrypt包下的类的警告。但是这里的*是不包含包分隔符的,也就是说它的子包里面的类是不会被禁止的。如果需要连它下面的包的类也一并禁止,可以使用包含包分隔符的**,也就是如下:

-dontwarn org.conscrypt.**

关于过滤器的更多用法,可以参阅文档:https://www.guardsquare.com/en/products/proguard/manual/usage#filters

总结

这是最常见的一类问题,我们来总结一下这个套路:

  1. 编译报错,提示内容是Warning: there were xxx unresolved references to classes or interfaces.
  2. 往上找带有Warning:的日志,句型结构为:Warning: xxxx.xxx.ABC: can't find referenced class xxx.xxx.XYZ
  3. 往混淆规则里添加-dontwarn 类名的规则。

运行时问题

我们再来看另一类问题。

当有人在讨论群里求助混淆相关的问题时,往往会有人说加-keep。那么-keep是作什么用呢?在什么情况下需要它呢?

在文章开头一句话介绍过,Proguard 是一个压缩、优化和混淆Java字节码文件的工具。也就是,它所做的不仅仅包括我们所知道的混淆,它会分析所有的类,找出没有用的类、字段、方法等把它们删除掉,从而达到对字节码的压缩及优化。而如果我们使用了反射去调用一些类或方法的话,它是不知道的,这样就会导致“误删”的情况。

所以当开启混淆之后,出现类找不到,方法找不到,属性找不到时,我们就要使用keep相关规则来把它们给留住啦。比如使用-keep class xxxx {*;}保留指定的类名及其成员。

keep规则有三种(Android新增加的@keep注解不谈,这里只讲规则文件的内容):

  • -keep 保留指定的类名及其成员
  • -keepclassmembers 只保留住成员,不能保留住类名
  • -keepclasseswithmembers 根据成员找到满足条件的所有类,保留它们的类名和成员名

比如我们使用了事件总线,为使方法能保留,我们可以添加如下的规则:

-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}

表示保留所有类中的有@org.greenrobot.eventbus.Subscribe注解的方法。<methods>是指方法,如果要保留字段,则使用<fields>

反正就是找不到类或成员,你就keep住。这里的keep规则也是很灵活的,比如除了上面指定的类,你也可以指定为接口:

-keepclassmembernames,allowobfuscation interface * {
@retrofit2.http.* &lt;methods&gt;;
}

可以指定继承自某个类的所有类,如:

-keep public class * extends android.app.Service

可以指定实现某个接口的类,如:

-keep class * implements com.google.gson.TypeAdapterFactory

可以指定某个包下的所有类(包括子包)及所有成员,如:

-keep class com.tencent.stat.**  {* ;}

除此之外,我们还可能会遇到其他问题,这里把其他常用的规则也快速过一下:

注解解析不到?

-keepattributes *Annotation*

泛型转换失败?

-keepattributes Signature

枚举?

-keepclassmembers enum * { *; }

掌握以上几条规则,通常可以解决绝大多数混淆以后运行出错的问题了。

本篇讲到这里,希望对被以上常见问题所困扰的同学有所帮助。这里需要注意一下,对于第一类问题,在编译的时候就能暴露出来,而第二类问题,只有在运行到相关代码时才会出现。所以如果一个项目以前是不使用混淆的话,在启用混淆之后一定要做好回归测试。

ProGuard常见问题及解决套路的更多相关文章

  1. NHibernate常见问题及解决方法

    NHibernate常见问题及解决方法 曾经学过NHibernate的,但是自从工作到现在快一年了却从未用到过,近来要巩固一下却发现忘记了许多,一个"in expected: <end ...

  2. WebView加载本地html、js文件常见问题及解决办法

    声明:基于android studio平台,php语言搭建服务器 目录: 一.JavaScript脚本语言没有反应 二.alert无法弹出 三.html页面之间不能跳转 四.屏幕缩放没有达到预期效果 ...

  3. 转:WebTest的常见问题与解决

    WebTest的常见问题与解决录制好一个WebTest,加上各种规则,编辑后运行并不会像我们想象的那么顺利成功,往往会碰到很多问题,运行不成功的情况比较多,这样我们就遇到了如何解决这些问题的情形.1. ...

  4. WordPress源代码压缩优化及常见问题的解决

    先来看看效果: 意思就是让你的源代码看起来都挤在一起,这样如果别人想看你的源代码的话就不容易看懂了,(当然如果别人实在想看的话也可以通过某些软件的整理代码的功能来实现,比如IDEA的Ctrl+alt+ ...

  5. FineUIMvc 常见问题及解决办法

    Ø  简介 FineUIMvc 是基于 jQuery 的专业 ASP.NET MVC/Core 控件库,本文主要介绍 FineUIMvc 的常见问题及解决办法. 1.   View 中无法调用 Htm ...

  6. rsync @ERROR: auth failed on module backup 解决思路及附录rsync常见问题及解决办法

    昨晚小版本上线,使用rsync往服务器上传文件时,client报如下异常: @ERROR: auth failed on module backup rsync error: error starti ...

  7. fetch使用的常见问题及其解决办法

    摘自: https://segmentfault.com/a/1190000008484070 fetch使用的常见问题及其解决办法 javascript wonyun 2月25日发布 |   0 收 ...

  8. C#用ado.net访问EXCEL的常见问题及解决方法

    C#用ado.net访问EXCEL的常见问题及解决方法,除了像sql server,access常见的数据库,其实Excel文件也可以做为数据库访问. ado.net访问excel的实例: OleDb ...

  9. PHPmailer发送邮件时的常见问题及解决办法

    来源:http://www.chinastor.com/a/jishu/mailserver/0G392262014.html 使用PHPmailer发送邮件时的常见问题总结: 一,没有定义发送邮箱$ ...

随机推荐

  1. 获取浏览器端的cookie方法

    代码如下: function getCookie(key){ var cookies=document.cookie; if(cookies.length>0){ var start=cooki ...

  2. Parameter ‘brOrderNo’ not found

    org.apache.ibatis.binding.BindingException: Parameter 'brOrderNo' not found. Available parameters ar ...

  3. SyntaxError Generator expression must be parenthesized

    环境: Windows10 python3.7.0 Django1.11.15 异常 启动Django时抛出以下异常: Unhandled exception in thread started by ...

  4. [JZOJ]100046【NOIP2017提高A组模拟7.14】收集卡片

    Star 计划订购一本将要发行的周刊杂志,但他可不是为了读书,而是-- 集卡. 已知杂志将要发行 N 周(也就是 N 期),每期都会附赠一张卡片.Star 通 过种种途径,了解到 N 期杂志附赠的卡片 ...

  5. django开发微信公众平台遇到的问题记录

    在pythonanywhere.com上使用django开发微信公众平台应用,结果用户发送的信息,微信服务器一次也没有成功转发到pythonanywhere上来,但是用接口测试工具调试却发现是正常的, ...

  6. mysql 全表扫描、全索引扫描、索引覆盖(覆盖索引)

    full index scan:全索引扫描,查询时,遍历索引树来获取数据行.如果数据不是密集的会产生随机IO 在执行计划中是Type列,index full table scan:通过读物理表获取数据 ...

  7. Apache Mahout 0.9、10.1、11. CardinalityException: Required cardinality 60 but got 29

      我们可以使用Apache Mahout来快速创建高效扩展性又好的机器学习应用.Mahout结合了诸如H2O算法.Scala.Spark和Hadoop MapReduce等模块,为开发人员提供了一个 ...

  8. [using_microsoft_infopath_2010]Chapter4 使用SharePoint列表表单

    本章概要: 1.把SharePoint列表表单转换成InfoPath可用形式 2.使用字段和控件 3.规划表单布局 4.理解列表表单的局限性

  9. HDU 4313 Contest 2

    很明显的树形DP了.但网上有的说可以用并查集.... 考虑一棵子树,当根结点有机器人时,则必定所有子树都要和根结点断开,而根结点向上返回的路径值则为其父结点与根结点连边的权值. 当根结点安全时,假设其 ...

  10. [Tools] Using mobile device for debugging your mobile web site

    1. First you have enable "Developer mode" on your mobile device. (Different device might b ...