目前有很多的工具比如apktool等可以对代码进行反编译获得某个Apk的源码,所以我们为了防止代码被盗用,我们在释放版本前需要对代码进行混淆处理。Android SDK已经提供了混淆工具Proguard的支持。Proguard 是一个开源的混淆工具,它的作用简单得说就是通过对代码进行混淆来加大被反编译的难度,即使反编译成功也很难获取到代码的真正语义,使得代码更加的安全。混淆是不可逆的,在混淆过程中将会丢失一些不影响正常运行的信息。除了保护代码不被反编译外还可以精简编译后的程序大小。

添加到项目中

在使用Android Studio创建新的项目的时候,AS会为我们自动添加对Proguard支持,如下所示:

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

我们的sdk已经为我们提供了两个默认的配置文件proguard-android.txt和proguard-android-optimize.txt。我们可以根据这两个文件根据自己的需求进行修改。这两个文件位于[sdk path]/tools/proguard/ 目录下。
这两个有什么区别呢根据proguard-android-optimize.txt上面的注释来看proguard-android.txt是关闭了优化标志的,如果不需要优化,那么可以使用关闭优化标志的proguard-android.txt配置文件而不是proguard-android-optimize.txt。添加优化将会引入某些风险,并不是所有的优化都能在Dalvik所有版本上运行。
这个目录下还有其他目录存放的内容如下所示:

- bin :该目录下存放的是运行Proguard的脚本,分别有两种模式一种是纯粹的脚本形式proguard.sh,一种是有GUI界面的形式proguardgui.sh。
- lib : 一些主要的jar包可以使用 "java -jar ...."形式运行
- docs: 一些Progurd使用文档,如果打算深入学习Progurd可以借助这些文档。
- examples : 一些配置例子文件。

上面gradle中配置的意思是它会使用sdk提供的proguard-android.txt 以及项目app中的proguard-rules.pro文件中的配置,所以我们可以在proguard-rules.pro进行我们项目专用的混淆配置。

还需要注意的是只有在release模式下打包apk时才会自动运行ProGuard对代码进行混淆,而在debug模式下并不会调用proguard对代码进行混淆。

下面是sdk提供的proguard-android.txt的内容

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html

# 不使用大小写混合方式命令类名
-dontusemixedcaseclassnames
# 是否混淆第三方jar
-dontskipnonpubliclibraryclasses
# 混淆时是否记录日志
-verbose

# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
# 不优化输入的类文件
-dontoptimize
# 混淆时是否做预校验
-dontpreverify
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file.
# 保护注解
-keepattributes *Annotation*
# 保持哪些类不被混淆
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
# 保持指定类的成员 这里是保持 View中的get set方法不被混淆
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}

# 保持Activity中onClick使用到的方法
# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}

# 保持枚举 enum 类不被混淆
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}

# 保持 Parcelable 不被混淆
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# 保持静态类不被混淆
-keepclassmembers class **.R$* {
public static <fields>;
}

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**

下面是在网上找的一些配置时候常见的标签,这里放在这个地方供大家配置的时候查看:

-include {filename}               从给定的文件中读取配置参数
-basedirectory {directoryname} 指定base目录
-injars {class_path} 指定要处理的应用程序jar和目录
-outjars {class_path} 指定处理完后要输出的jar和目录的名称
-libraryjars {classpath} 指定要处理的应用程序jar和目录所需要的程序库文件
-dontskipnonpubliclibraryclasses 指定不去忽略非公共的库类。
-dontskipnonpubliclibraryclassmembers 指定不去忽略包可见的库类的成员。

保留选项
-keep {Modifier} {class_specification} 保持指定的类文件和类的成员
-keepclassmembers {modifier} {class_specification} 保持指定类的成员
-keepclasseswithmembers {class_specification} 保持指定的类和类的成员,但条件是所有指定的类和类成员是要存在.
-keepnames {class_specification} 保持指定的类和类的成员的名称
-keepclassmembernames {class_specification} 保持指定的类的成员的名称
-keepclasseswithmembernames {class_specification} 保持指定的类和类的成员的名称
-printseeds {filename} 列出类和类的成员-keep选项的清单,标准输出到给定的文件

压缩
-dontshrink 不压缩输入的类文件
-printusage {filename}
-whyareyoukeeping {class_specification}

优化
-dontoptimize 不优化输入的类文件
-assumenosideeffects {class_specification} 优化时假设指定的方法,没有任何副作用
-allowaccessmodification 优化时允许访问并修改有修饰符的类和类的成员

混淆
-dontobfuscate 不混淆输入的类文件
-printmapping {filename}
-applymapping {filename} 重用映射增加混淆
-obfuscationdictionary {filename} 使用给定文件中的关键字作为要混淆方法的名称
-overloadaggressively 混淆时应用侵入式重载
-useuniqueclassmembernames 确定统一的混淆类的成员名称来增加混淆
-flattenpackagehierarchy {package_name} 重新包装所有重命名的包并放在给定的单一包中
-repackageclass {package_name} 重新包装所有重命名的类文件中放在给定的单一包中
-dontusemixedcaseclassnames 混淆时不会产生形形色色的类名
-keepattributes {attribute_name,...} 保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
-renamesourcefileattribute {string} 设置源文件中给定的字符串常量

在混淆后会在\app\build\outputs\mapping\release目录中产生一个mapping.txt文件它列出了原始的类,方法和字段名与混淆后代码间的映射。当从release版本中收到一个bug报告时,可以用它来翻译被混淆的代码,在Bugly以及网易云捕中都会用到这个文件。

Contents
  1. 1. 添加到项目中