ProGuardis the most popular optimizer for Java bytecode. It makes your Java and Androidapplications up to 90% smaller and up to 20% faster. ProGuard also providesminimal protection against reverse engineering by obfuscating the names ofclasses, fields and methods.
上面是proguard官方网站给ProGuard的优势做出的总结。大致意思是:ProGuard技术可以使得java项目或者android应用的体量减少10%,并且使其运行的速度加快20%。并且ProGuard还提供最低限度的保护对逆向工程通过混淆类的名称、字段和方法。那么ProGuard到底是什么样的一种技术呢?为什么有如此好的效果?结合本人在项目中的实际应用,简单介绍一下ProGuard技术和它在Android工程中的应用。
(一)ProGuard概念和基本功能
ProGuard是中免费的、开源的Java项目,提供了四个基本的功能:Shrink(压缩)、Optimize(优化)、Obfuscate(混淆)和Preveirfy(预检)。利用以上四个功能达到java项目或者android项目瘦身、提高运行流畅度和维护代码安全性的目的。
(二)基本的工作原理
上图是ProGuard官方给出的基本工作原理流程图。
1)Shrink(压缩):
作为工作的第一个环节,ProGuard会自动检查项目中没有用到的类、方法、属性和字段。在程序编译时自动移除,从而达到java工程瘦身的目的。
2)Optimize(优化)
在这个阶段,java代码已经转变为可以在JVM直接运行的字节码。ProGuard可以对字节码进行功能优化,并且可以忽略和移除无用的字节指令。
3)Obfuscate(混淆)
这一步是提高代码安全性的关键所在。这一步,ProGuard会将程序中正常的代码转化为用a.b.c这样没有任何实际意义的字母的随意组合。这样的话,从apk文件进行反编译的时候,我们得到的将不是原来有条不紊的工程,而是一块块没有任何意义的凌乱的字母组合片段。
4)Preverify(预检)
主要是在java代码平台上对处理后的代码进行预检。
详细的技术原理可以到ProGuar官方网站https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html进行查看。
(三)基本的使用
如果是安装Android Studio的小伙伴可以不必到去官方去下载jar,因为Android Studio已经内在地集成了这个功能。可以在sdk\tools\proguard安装文件中找到我们的AS集成的proguard安装包。并且,我们可以在build.gradle文件中看到
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
说明我们的功能已经默认地支持ProGuard技术。如果需要开启这一神奇的功能,将minifyEnabled属性设置为true。
(四)设置Proguard文件
下面简要介绍一下ProGuard文件的大概要点,主要包括混淆文件的基本配置和如何保留不需要混淆的类。
a) 混淆代码的基本配置,该配置信息是android工程都需要使用的,可以作为代码混淆的模板使用。基本的指令和说明如下
# 代码混淆压缩比,在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选项
b) 保留不需要混淆的类。具体的说明如下。
首先,应该保留四大组件以及自定义的Application等基本类,因为这些类及其子类可能被外部调用到
keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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
除此之外,在开发中我们有时候会引入一些开源的代码库,由于这些文件是开源的,在安全性方面往往没有那么严格,可以不必混淆。还有些情况下,需要在Intent或Binder中传递的实体类,经过代码混淆后往往会发生错误,也需要关闭对其代码混淆。除了上述情况,像枚举类型、使用了Gson类工具的实体类、使用了WebView的JavaScript调用代码、采用了反射机制的类等也不要进行代码的混淆。这些情况的基本设置及其说明如下
# 保留support下的所有类及其内部类
-keep class android.support.** {*;}
# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
# 保留R下面的资源
-keep class **.R$* {*;}
# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在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;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# webView处理,项目中没有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview { public *; }
-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);
}
上述即是Android Studio中ProGuard基本使用说明,希望对想采用该技术进行代码加固的朋友带来帮助。