ProGuard代码混淆技术详解
前言
内容目录
- ProGuard简介
- ProGuard工作原理
- 如何编写一个ProGuard文件
- 其他注意事项
- 小结
ProGuard简介
- 压缩(Shrink):检测并移除代码中无用的类、字段、方法和特性(Attribute)。
- 优化(Optimize):对字节码进行优化,移除无用的指令。
- 混淆(Obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名。
- 预检(Preveirfy):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。
ProGuard工作原理

如何编写一个ProGuard文件
- 基本混淆
- 针对APP的量身定制
- 针对第三方jar包的解决方案
基本混淆
# 代码混淆压缩比,在0和7之间,默认为5,一般不需要改
-optimizationpasses 5 # 混淆时不使用大小写混合,混淆后的类名为小写
-dontusemixedcaseclassnames # 指定不去忽略非公共的库的类
-dontskipnonpubliclibraryclasses # 指定不去忽略非公共的库的类的成员
-dontskipnonpubliclibraryclassmembers # 不做预校验,preverify是proguard的4个步骤之一
# Android不需要preverify,去掉这一步可加快混淆速度
-dontpreverify # 有了verbose这句话,混淆后就会生成映射文件
# 包含有类名->混淆后类名的映射关系
# 然后使用printmapping指定映射文件的名称
-verbose
-printmapping proguardMapping.txt # 指定混淆时采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不改变
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 保护代码中的Annotation不被混淆,这在JSON实体映射时非常重要,比如fastJson
-keepattributes *Annotation* # 避免混淆泛型,这在JSON实体映射时非常重要,比如fastJson
-keepattributes Signature //抛出异常时保留代码行号,在异常分析中可以方便定位
-keepattributes SourceFile,LineNumberTable -dontskipnonpubliclibraryclasses用于告诉ProGuard,不要跳过对非公开类的处理。默认情况下是跳过的,因为程序中不会引用它们,有些情况下人们编写的代码与类库中的类在同一个包下,并且对包中内容加以引用,此时需要加入此条声明。 -dontusemixedcaseclassnames,这个是给Microsoft Windows用户的,因为ProGuard假定使用的操作系统是能区分两个只是大小写不同的文件名,但是Microsoft Windows不是这样的操作系统,所以必须为ProGuard指定-dontusemixedcaseclassnames选项
2,需要保留的东西
# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
} # 保留了继承自Activity、Application这些类的子类
# 因为这些子类,都有可能被外部调用
# 比如说,第一行就保证了所有Activity的子类不要被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService # 如果有引用android-support-v4.jar包,可以添加下面这行
-keep public class com.xxxx.app.ui.fragment.** {*;} # 保留在Activity中的方法参数是view的方法,
# 从而我们在layout里面编写onClick就不会被影响
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
} # 枚举类不能被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
} # 保留自定义控件(继承自View)不被混淆
-keep public class * extends android.view.View {
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
} # 保留Parcelable序列化的类不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
} # 保留Serializable序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
} # 对于R(资源)下的所有类及其方法,都不能被混淆
-keep class **.R$* {
*;
} # 对于带有回调函数onXXEvent的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
}
针对APP的量身定制
# 保留实体类和成员不被混淆
-keep public class com.xxxx.entity.** {
public void set*(***);
public *** get*();
public *** is*();
}
一种好的做法是把所有实体都放在一个包下进行管理,这样只写一次混淆就够了,避免以后在别的包中新增的实体而忘记保留,代码在混淆后因为找不到相应的实体类而崩溃。
2,内嵌类
内嵌类经常会被混淆,结果在调用的时候为空就崩溃了,最好的解决方法就是把这个内嵌类拿出来,单独成为一个类。如果一定要内置,那么这个类就必须在混淆的时候保留,比如如下:
# 保留内嵌类不被混淆
-keep class com.example.xxx.MainActivity$* { *; }
这个$符号就是用来分割内嵌类与其母体的标志。
3,对WebView的处理
# 对WebView的处理
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String)
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, java.lang.String)
}
4,对JavaScript的处理
# 保留JS方法不被混淆
-keepclassmembers class com.example.xxx.MainActivity$JSInterface1 {
<methods>;
}
其中JSInterface是MainActivity的子类
5,处理反射
Class.forName("SomeClass")SomeClass.classSomeClass.class.getField("someField")SomeClass.class.getDeclaredField("someField")SomeClass.class.getMethod("someMethod", new Class[] {})SomeClass.class.getMethod("someMethod", new Class[] { A.class })SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField")AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")
在混淆的时候,要在项目中搜索一下上述方法,将相应的类或者方法的名称进行保留而不被混淆。
针对第三方jar包的解决方案
# 针对android-support-v4.jar的解决方案
-libraryjars libs/android-support-v4.jar
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
2,其他的第三方jar包的解决方案
这个就取决于第三方包的混淆策略了,一般都有在各自的SDK中有关于混淆的说明文字,比如支付宝如下:
# 对alipay的混淆处理
-libraryjars libs/alipaysdk.jar
-dontwarn com.alipay.android.app.**
-keep public class com.alipay.** { *; }
值得注意的是,不是每个第三方SDK都需要-dontwarn 指令,这取决于混淆时第三方SDK是否出现警告,需要的时候再加上。
其他注意事项
- 测试工作要基于混淆包进行,才能尽早发现问题
- 每天开发团队的冒烟测试,也要基于混淆包
- 发版前,重点的功能和模块要额外的测试,包括推送,分享,打赏
@keep
@keepPublicGetterSetters
public class Bean{
public boolean booleanProperty;
public int intProperty;
public String stringProperty;
}
小结
阅读扩展
ProGuard代码混淆技术详解的更多相关文章
- Android ProGuard代码混淆技术详解
前言 受<APP研发录>启发,里面讲到一名Android程序员,在工作一段时间后,会感觉到迷茫,想进阶的话接下去是看Android系统源码呢,还是每天继续做应用,毕竟每天都是画UI ...
- CDN学习笔记二(技术详解)
一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精华放上网.公诸同 ...
- CDN技术详解及实现原理
CDN技术详解 一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精 ...
- 腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践
本文来自腾讯前端开发工程师“ wendygogogo”的技术分享,作者自评:“在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦.” 1.GIF格式的历史 GIF ( Gr ...
- 单元测试系列之四:Sonar平台中项目主要指标以及代码坏味道详解
更多原创测试技术文章同步更新到微信公众号 :三国测,敬请扫码关注个人的微信号,感谢! 原文链接:http://www.cnblogs.com/zishi/p/6766994.html 众所周知Sona ...
- IPv6技术详解:基本概念、应用现状、技术实践(下篇)
本文来自微信技术架构部的原创技术分享. 1.前言 在上篇<IPv6技术详解:基本概念.应用现状.技术实践(上篇)>,我们讲解了IPV6的基本概念. 本篇将继续从以下方面展开对IPV6的讲解 ...
- IPv6技术详解:基本概念、应用现状、技术实践(上篇)
本文来自微信技术架构部的原创技术分享. 1.前言 普及IPV6喊了多少年了,连苹果的APP上架App Store也早已强制IPV6的支持,然并卵,因为历史遗留问题,即使在IPV4地址如果饥荒的情况下, ...
- Java基础-反射(reflect)技术详解
Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制 如下图所示,JVM类加载机制分为五个部分 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
随机推荐
- 彻底理解ThreadLocal二
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...
- 快速学习C语言一: Hello World
估计不会写C语言的同学也都听过C语言,从头开始快速学一下吧,以后肯定能用的上. 如果使用过其它类C的语言,如JAVA,C#等,学C的语法应该挺快的. 先快速学习并练习一些基本的语言要素,基本类型,表达 ...
- Enum是如何用的?
一.前言 对于枚举Enum,大家都非常熟悉,但枚举出现的场景非常多的时候,是不是可以抽象出一个通用的解决方式.代码大家都会写,但并不是所有人都喜欢写重复的代码,老是用Ctrl+C和Ctrl+V累不累啊 ...
- 创建widget
1. 定义方法 def predictAll(tickers, startdt='36', enddt = 'today', predictdays = 1): if enddt == 'today' ...
- AJAX原理总结
AJAX全称 Asynchronous JavaScript and XML(异步的JavaScript 和XML) 同步和异步 异步传输是面向字符的传输,单位是字符 同步传输是面向比特,单位是帧,传 ...
- CSS Font知识整理总结
1.什么是字体 字体是文字的外在形式,就是文字的风格,是文字的外衣.比如行书.楷书.草书,都是一种字体.同样一个字每个人写起来都会有差异,可以说每个人都有一套潜在的字体库.对于web页面来说,字体就是 ...
- Wix 安装部署教程(七) 获取管理员权限
应用程序运行的时候,难免会读写文件,产生新的数据.但Program Files下的文件是不能随便更改,Win7下如果没有权限,将会被拒绝.我现在有两种方式,一种是将数据路径移到Program Data ...
- c++实现冒泡排序
# include<iostream> #include<stdio.h> using namespace std; void maopao(int *list){ int i ...
- JavaScript获取两个数之间的任意随机数
通过JavaScript的Math.random()方法可以获取0到1之间的任意随机数,那如何获取任意给定的两个数之间的随机数呢?如获取2和5之间的随机数,5和10之间的随机数等. 由于Math.ra ...
- 关于基本类型值和引用类型值以及Vue官方API的array.$remove(reference)
今天又是孟哥解惑. 数组里的元素也是指向内存地址么? 这个要分情况的. 无论a[0],a[2]在什么地方,只要其值是基本类型值,就是值的比较,只要其值是引用类型(对象),就是内存地址的比较. Vue官 ...