ProGuard可以经过压缩、优化、混淆、预检等操做,检测并删除未使用的类,字段,方法和属性,分析和优化字节码,使用简短无心义的名称来重命名类,字段和方法。从而使代码更小、更高效、更难进行逆向工程。 html
上图就是ProGuard的工做流程,分别会通过四个阶段:java
压缩(Shrink)
:在压缩处理这一步中,用于检测和删除没有使用的类,字段,方法和属性优化(Optimize)
:在优化处理这一步中,对字节码进行优化,而且移除无用指令混淆(Obfuscate)
:在混淆处理这一步中,使用a,b,c等无心义的名称,对类,字段和方法进行重命名预检(Preveirfy)
:在预检这一步中,主要是在Java平台上对处理后的代码进行预检以上四个步骤都是可选的,咱们能够经过配置脚原本决定其中的哪几个步骤。好比咱们能够配置只压缩和混淆,不进行优化,不进行预检。 ProGuard的官网有使用指导:proguard.sourceforge.net/android
ProGuard能够经过命令行调用,如:web
执行成功后,用jd-gui打开处理后的jar文件:算法
能够发现,类已经被混淆处理了。windows
这里,咱们引入Entry points的概念。Entry points是在ProGuard过程当中不会处理的类或者方法。 在Shrink的步骤中,ProGuard会递归遍历,搜索使用了哪些类和成员,对于没有使用的类和类成员,就会在压缩阶段丢弃。 接下来在Optimize阶段,那些非Entry points的类、方法都会被设置为private、static或者final,没有使用的参数会被移除,此外,有些方法会被标记为内联。 在Obfuscate的步骤中,ProGuard会对非Entry points的类和方法进行重命名。bash
Includedescriptorclasses
:通常用于保证native方法名,确保方法的参数类型不会重命名,确保方法签名不会被改变,这样才能跟native libraries相匹配。Allowshrinking
:容许压缩Allowoptimization
:容许优化Allowobfuscation
:容许混淆名称Class Specifications是用来描述类和方法的模板,下面是这个模板的格式:app
其中,[]中的内容是可选,名称可使用通配符,匹配构造函数、匹配成员、匹配方法,详细请参考:proguard.sourceforge.net/manual/usag…ide
在配置文件中出现的相对路径均是相对于该路径,如图: 函数
指定处理的jar包(或者aars, wars, ears, zips, apks, directories)等,这个jar包里面的类将会被ProGuard处理并写入到输出的jar包里去。通常非class文件会不作任何处理直接直接复制到输出文件中,injars能够屡次使用,引入不一样的须要处理的文件。 注意,该选项能够指定一个目录,那么该目录下全部文件都会被看成input file处理。
设置处理完成后的输出文件路径
指定要处理应用程序的jar(或者aars, wars, ears, zips, apks, directories),这些文件不会包含到输出文件中。通常是指被处理文件所依赖的一些jar包,而那些jar包是不须要被处理以及写入到输出文件的。好比:
忽略library里面非public修饰的类。从而加快ProGuard的处理速度和下降ProGuard的使用内存。通常而言,library里的非公开类是不能被程序使用的,忽略掉这些类能够加快混淆速度。可是请注意,有一种特殊状况:有些人编写的代码与类库中的类在同一个包下,并且对该包的非public类进行了使用,在这种状况下,就不能使用该选项了。
不忽略library里面非public修饰的类
指定不忽略非public类里面的成员和方法。ProGuard默认会忽略类库里非public类里的成员和方法,可是因为一些3.2.5里面的一些缘由,应用程序里可能会用到这些,这时候就须要这个选项来指定不忽略它们。
指定要保留在输出文件内的目录。默认状况下,目录会被移除。这会减小输出文件的大小,但若是你的代码引用到它们时可能会致使程序崩溃(如mypackage.MyCalss.class.getResource(""))。这时就须要指定-keepdirectories mypackage。-keepdirectories mydirectory匹配 mydirectory 目录;-keepdirectories mydirectory/*匹配 mydirectory 的直接子目录;-keepdirectorie mydirectory/**匹配全部子目录,若是没有指定过滤器,全部目录会被保留。
指定被处理class文件所使用的java版本,可选的有: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 (or just 5), 1.6 (or just 6), 1.7 (or just 7), or 1.8 (or just 8).
强制输出,即便输出文件已是最新状态
指定该类以及类的成员和方法为entry points,不被ProGuard混淆
指定类的某些成员不被混淆,注意类名仍是会被混淆,如:
经过成员来指定哪些类不被混淆处理。好比能够用来保留包含main方法的类:
若是指定了多条规则,以下,那么必须同时包含sayHello和test两个方法的类才会被保留
-keepclassmembers,allowshrinking class_specification的别名,保留名称不被混淆,但能够被压缩
-keepclasseswithmembers,allowshrinking class_specification的别名,保留名称不被混淆,但能够被压缩
-keepclasseswithmembers,allowshrinking class_specification的别名
把keep匹配的类和方法输出到文件中,能够用来验证本身设定的规则是否生效.
指定不进行压缩.
把没有使用的代码输出到文件中,方便查看哪些代码被压缩丢弃了。.
指定不对输入代码进行优化处理。优化选项是默认打开的。
指定混淆是采用的算法,后面的参数是一个过滤器,这个过滤器是谷歌推荐的算法,通常不作更改
指定优化的级别,在0-7之间,默认为5.
能够指定移除哪些方法没有反作用,如在android开发中,如想在release版本能够把全部log输出都移除,能够配置:
那么全部log代码将会在优化阶段被去除。
指定不进行混淆
生成map文件,记录混淆先后的名称对应关系,注意,这个比较重要,由于混淆后运行的名称会变得不可读,只有依靠这个map文件来还原。
主要是用来维持两次混淆公用一份mapping,确保相同的代码先后两次混淆后是同样的命名
指定外部模糊字典
指定class模糊字典.
指定package模糊字典
类和成员混淆的时候,使用惟一的名字
不使用大小写混合类名,注意,windows用户必须为ProGuard指定该选项,由于windows对文件的大小写是不敏感的,也就是好比a.java和A.java会认为是同一个文件。若是不这样作而且你的项目中有超过26个类的话,那么ProGuard就会默认混用大小写文件名,致使class文件相互覆盖。
保持packagename 不混淆
指定从新打包,全部包重命名,这个选项会进一步模糊包名,将包里的类混淆成n个再从新打包到一个个的package中
将包里的类混淆成n个再从新打包到一个统一的package中,会覆盖flattenpackagehierarchy选项
混淆时可能被移除下面这些东西,若是想保留,须要用该选项,对于通常注解处理如 -keepattributes Annotation。
attribute_filter :
指定不执行预检
把全部信息都输出,而不只仅是输出出错信息
不输出指定类的错误信息.
不打印指定类的警告信息
遇到警告的时候,忽略警告继续执行ProGuard,不建议添加此项。
输出当前ProGuard所使用的配置
指定输出所处理的类的结构
在代码中,若是用到了反射,混淆会改变类和成员的名字,致使反射找不到相应的类或者方法,因此开发者在混淆的时候,必须把用到了反射的类保留,不进行混淆。通常而言,使用反射通常会有如下方式,能够搜索代码,找到相关的类,而后在混淆配置里面进行保留:
# reflectClass类使用了反射,保留该类
-keep class package.reflectClass { *; }
复制代码
#代码混淆压缩比,在0~7之间,默认为5,通常不作修改
-optimizationpasses 5
#指定混淆是采用的算法,后面的参数是一个过滤器,这个过滤器是谷歌推荐的算法,通常不作更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#混合时不使用大小写混合,混合后的类名为小写,windows下必须使用该选项
-dontusemixedcaseclassnames
#指定不去忽略非公共库的类和成员
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#输出详细信息
-verbose
#输出类名->混淆后类名的映射关系
-printmapping map.txt
#不作预校验,preverify是proguard的四个步骤之一,Android不须要preverify,去掉这一步可以加快混淆速度。
-dontpreverify
#保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
#避免混淆泛型
-keepattributes Signature
#抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
#保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保留枚举类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#保留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();
}
复制代码
另外,因为Android平台在使用混淆的时候,还要特别注意要添加如下一些配置:
#保留咱们使用的四大组件,自定义的Application等等这些类不被混淆
-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
#保留support下的全部类及其内部类
-keep class android.support.** {*;}
#保留R下面的资源
-keep class **.R$* {*;}
#保留在Activity中的方法参数是view的方法,
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
#保留咱们自定义控件(继承自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 *;
}
#对于带有回调函数的onXXEvent的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
}
#在咱们的app中使用了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, jav.lang.String);
}
# 在app中与HTML5的JavaScript的交互进行特殊处理,如
# package com.ljd.example;
#
# public class JSInterface {
# @JavascriptInterface
# public void callAndroidMethod(){
# // do something
# }
# }
#咱们须要确保这些js要调用的原生方法不可以被混淆,因而咱们须要作以下处理
-keepclassmembers class com.ljd.example.JSInterface {
<methods>;
}
#内嵌类常常被混淆,结果在调用的时候就崩溃了,若是须要保留内嵌类,则用如下方法来保留内嵌类,如暴力MyClass里面的内嵌类,$就是用来分割内嵌类和母体的标志
-keep class com.test.MyClass$* {*;}
#-----------如下处理反射类---------------
复制代码