一、混淆的意义
混淆代码并不是让代码无法被反编译,而是将代码中的类、方法、变量等信息进行重命名,把它们改成一些毫无意义的名字,同时也可以移除未被使用的类、方法、变量等。 所以直观的看,通过混淆可以提高程序的安全性,增加逆向工程的难度,同时也有效缩减了apk的体积。总结如下:
1、将项目中的类、方法、变量等信息进行重命名,变成一些无意义的简短名字。
2、移除未被使用的类、方法、变量等。
二、混淆的规则和配置
凡是需要在AndroidManifest.xml中去注册的所有类的类名以及从父类重写的方法名都自动不会被混淆。 因此,除了Activity之外,这份规则同样也适用于Service、BroadcastReceiver和ContentProvider。
在Project app module的build.gradle中有配置如下代码:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
复制代码
(1)minifyEnabled
minifyEnabled为true的作用:启用代码混淆、压缩APK;
minifyEnabled会对资源进行压缩,多余的代码在打包的时候就给移除掉了。不仅仅是代码,没有被调用的资源同样也会被移除掉,因此minifyEnabled除了混淆代码之外,还可以起到压缩APK包的作用。
开启混淆后也可添加shrinkResources,表示开启删除无用资源。
(2)shrinkResources
shrinkResources为true用来开启删除无用资源。
shrinkResources会对就是没有被引用的文件(经过实测是drawable,layout,实际并不是彻底删除,而是保留文件名,但是没有内容),但是因为需要知道是否被引用所以需要配合mififyEnable使用,只有当两者都为true的时候才会起到真正的删除无效代码和无引用资源的目的。
(3)避免被误删除
若配置minifyEnable true && shrinkResources true,防止避免误删除的文件,可以配置如下: 在res/raw/keep.xml(避免被误删除)写了配置的
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/activity_main,@drawable/comfirm_bg"/>
复制代码
对于没有被引用的文件&没有在配置文件keep.xml中声明保存的文件,打包后是没有内容的,声明保存的文件有内容(资源文件和layout文件)。 注意:string.xml中没有被引用的怎么设置都不会被删除。
(4)proguard-android.txt和proguard-rules.pro
proguard-android.txt:代表系统默认的混淆规则配置文件,该文件在<Android SDK目录>/tools/proguard下,一般不要更改该配置文件,因为也会作用于其它项目。
proguard-rules.pro:代码表当前Project的混淆配置文件,在app module下,可以通过修改该文件来添加适用当前项目的混淆规则。
(5)proguard文件夹
代码混淆生成apk之后,项目下面会多出来一个proguard文件夹,proguard文件夹中四个文件的作用。
dump.txt : 描述了apk中所有类文件中内部的结构体。
mapping.txt : 列出了原始的类、方法和名称与混淆代码间的映射。
seeds.txt : 列出了没有混淆的类和方法。
usage.txt : 列出congapk中删除的代码。
三、编写自己的混淆代码
-optimizationpasses 5 # 指定代码的压缩级别
-dontusemixedcaseclassnames # 是否使用大小写混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方jar
-dontpreverify # 混淆时是否做预校验
-verbose # 混淆时是否记录日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法
-ignorewarnings # 忽略警告
-keepattributes Signature# 保留泛型与反射
-keepattributes *Annotation* # 表示对注解中的参数进行保留
-keepattributes *JavascriptInterface* # 保留WebView与JavaScript交互代码
-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 com.android.vending.licensing.ILicensingService # 保持哪些类不被混淆
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
# 表示示不混淆任何一个View中的setXxx()和getXxx()方法,
# 因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
# 表示不混淆Activity中参数是View的方法。因为点击事件有这样一种写法,
# 在XML中配置android:onClick="buttonClick"属性,
# 当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,
# 如果这个方法被混淆的话就找不到了
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
# 保持枚举 enum 类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
# 保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
# 表示不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,
# 字段名要是被混淆了,id也就找不着了
-keepclassmembers class **.R$* {
public static <fields>;
# 保持自己定义的类不被混淆
-keep class MyClass;
复制代码