ProGuard常见问题及解决套路
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
总结
这是最常见的一类问题,我们来总结一下这个套路:
- 编译报错,提示内容是
Warning: there were xxx unresolved references to classes or interfaces.
。 - 往上找带有
Warning:
的日志,句型结构为:Warning: xxxx.xxx.ABC: can't find referenced class xxx.xxx.XYZ
- 往混淆规则里添加
-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.* <methods>;
}
可以指定继承自某个类的所有类,如:
-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常见问题及解决套路的更多相关文章
- NHibernate常见问题及解决方法
NHibernate常见问题及解决方法 曾经学过NHibernate的,但是自从工作到现在快一年了却从未用到过,近来要巩固一下却发现忘记了许多,一个"in expected: <end ...
- WebView加载本地html、js文件常见问题及解决办法
声明:基于android studio平台,php语言搭建服务器 目录: 一.JavaScript脚本语言没有反应 二.alert无法弹出 三.html页面之间不能跳转 四.屏幕缩放没有达到预期效果 ...
- 转:WebTest的常见问题与解决
WebTest的常见问题与解决录制好一个WebTest,加上各种规则,编辑后运行并不会像我们想象的那么顺利成功,往往会碰到很多问题,运行不成功的情况比较多,这样我们就遇到了如何解决这些问题的情形.1. ...
- WordPress源代码压缩优化及常见问题的解决
先来看看效果: 意思就是让你的源代码看起来都挤在一起,这样如果别人想看你的源代码的话就不容易看懂了,(当然如果别人实在想看的话也可以通过某些软件的整理代码的功能来实现,比如IDEA的Ctrl+alt+ ...
- FineUIMvc 常见问题及解决办法
Ø 简介 FineUIMvc 是基于 jQuery 的专业 ASP.NET MVC/Core 控件库,本文主要介绍 FineUIMvc 的常见问题及解决办法. 1. View 中无法调用 Htm ...
- rsync @ERROR: auth failed on module backup 解决思路及附录rsync常见问题及解决办法
昨晚小版本上线,使用rsync往服务器上传文件时,client报如下异常: @ERROR: auth failed on module backup rsync error: error starti ...
- fetch使用的常见问题及其解决办法
摘自: https://segmentfault.com/a/1190000008484070 fetch使用的常见问题及其解决办法 javascript wonyun 2月25日发布 | 0 收 ...
- C#用ado.net访问EXCEL的常见问题及解决方法
C#用ado.net访问EXCEL的常见问题及解决方法,除了像sql server,access常见的数据库,其实Excel文件也可以做为数据库访问. ado.net访问excel的实例: OleDb ...
- PHPmailer发送邮件时的常见问题及解决办法
来源:http://www.chinastor.com/a/jishu/mailserver/0G392262014.html 使用PHPmailer发送邮件时的常见问题总结: 一,没有定义发送邮箱$ ...
随机推荐
- Codeforces Round #493 (Div. 2) A. Balloons 贪心水题
由于是输出任意一组解,可以将价值从小到大进行排序,第一个人只选第一个,第二个人选其余的.再比较一下第一个人选的元素和第二个人所选元素和是否相等即可.由于已将所有元素价值从小到大排过序,这样可以保证在有 ...
- Web API Filter
在Web Api中,有三种Filter Filter类型 实现的接口 描述 Authorization IAuthorizationFilter 最先运行的Filter,被用作请求权限校验 Actio ...
- 配置HTTPS加密的快速参考指南
Nginx ssl_protocols TLSv1 TLSv1.1 TLSv1.2 阿帕奇 SSLProtocol All -SSLv2 -SSLv3 密码套房 选择密码套件可能很困难,它们的名称可能 ...
- 【HiJ1m】在NOIP2017前写过的有用的东西汇总
http://www.cnblogs.com/Elfish/p/7544623.html 高级树状数组 http://www.cnblogs.com/Elfish/p/7554420.html BST ...
- Jquery Map遍历
var map = { 地名: ["北京","天津","上海"], 民族: ["汉族","藏族",& ...
- Crontab入门基础
Crontab入门基础 crontab前言 crontab是Unix和Linux用于设置周期性被执行的指令,是互联网很常用的技术,很多任务都会设置在crontab循环执行,如果不使用crontab,那 ...
- 微信支付报ip错,怀疑是因为不能正确获取$_Server[addr])ip导致的
报如下错误,应该是本地测试环境不能正确获取客户ip导致的错误 果然 放到服务器上在测试就好了
- 洛谷——P1886 滑动窗口|| POJ——T2823 Sliding Window
https://www.luogu.org/problem/show?pid=1886#sub || http://poj.org/problem?id=2823 题目描述 现在有一堆数字共N个数字( ...
- ORA-38760: This database instance failed to turn on flashback database
ORA-38760: This database instance failed to turn on flashback database 问题背景: 測试数据库运行shutdown ...
- jdk1.8Option类
目的:为了解决一个方法返回的参数可能为空而无法传入到新的方法做参数的问题,java8产生了新的内容:Option. 定义:Option是一个可以为空的容器对象(注意本质上是个万能对象). 常用方法:1 ...