一、定义
将代码转为一种难以理解和阅读的形式。
二、原因
1、【优化】它能优化java的字节码,使程序运行更快;
2、【压缩】缩减App大小,在混淆过程中它会找出未被使用过的类和类成员并删除他们;
3、【混淆】将代码中的类、函数、变量名随机变成无意义的代号形如:a,b,c...之类的,即使我们的APP即使被反编译,也不容易理解和阅读。
三、AndroidStudio混淆
Android SDK中自带了混淆工具,在SDK -> tools ->proguard。
1、在app下build.gradle中将混淆打开。
android {
compileSdkVersion 23
buildToolsVersion "24.0.1"
defaultConfig {
}
buildTypes {
release {
minifyEnabled true //混淆打开
zipAlignEnabled true //优化代码
shrinkResources true //优化资源
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
....
}
- minifyEnabled true :开启混淆,删除一些没有引用到的代码,开启混淆编译会变慢因此debug的时候需要关掉;
- zipAlignEnabled true:对打包的应用程序进行优化,能优化java字节码,提高运行效率,发布之前一定要设置该项为true;
- shrinkResources true:删除无用资源,也就是没有被引用的文件(drawable,layout,并不是彻底删除,而是保留文件名,但是没有内容);
shrinkResources要和minifyEnabled搭配使用,如果设置minifyEnabled 为false,shrinkResources设为true会报如下错误:
Removing unused resources requires unused code shrinking to be turned on.
更多使用说明可参考:shrinkResources 的使用
proguard-android.txt:AndroidStudio默认自动导入的规则,这个文件位于Android SDK根目录\tools\proguard\proguard-android.txt,这里面是一些比较常规的不能被混淆的代码规则。
proguard-rules.pro:对我们自己的项目需要特别定义混淆规则,它位于项目app目录下面(app/ proguard-rules.pro),里面的内容需要我们自己编写。默认情况下会对所有代码,包括第三方包都进行混淆,但有些代码或者第三方包是不能混淆的,这就需要我们手动编写混淆规则来保持不能被混淆的部分。
2、proguard-android.txt说明
下面默认的规则中指示了些需要保持不能别混淆的代码。
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
# 混淆时不使用大小写混合类名
-dontusemixedcaseclassnames
# 不跳过library中的非public的类,混淆第三方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方法的类的类名以及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中的setXxx()和getXxx()方法,以保证属性动画正常工作
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
# We want to keep methods in Activity that could be used in the XML attribute onClick
# 不混淆Activity中参数是View的方法,例如,一个控件通过android:onClick="clickMethodName"绑定点击事件,混淆后会导致点击事件失效
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
# 不混淆枚举类中的values()和valueOf()方法
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 不混淆Parcelable实现类中的CREATOR字段,以保证Parcelable机制正常工作
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
# 不混淆R文件中的所有静态字段,以保证正确找到每个资源的id
-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.
# 不对android.support包下的代码警告(如果我们打包的版本低于support包下某些类的使用版本,会出现警告的问题)
-dontwarn android.support.**
# Understand the @Keep support annotation.
# 不混淆Keep类
-keep class android.support.annotation.Keep
# 不混淆使用了注解的类及类成员
-keep @android.support.annotation.Keep class * {*;}
# 如果类中有使用了注解的方法,则不混淆类和类成员
-keepclasseswithmembers class * {
@android.support.annotation.Keep <methods>;
}
# 如果类中有使用了注解的字段,则不混淆类和类成员
-keepclasseswithmembers class * {
@android.support.annotation.Keep <fields>;
}
# 如果类中有使用了注解的构造函数,则不混淆类和类成员
-keepclasseswithmembers class * {
@android.support.annotation.Keep <init>(...);
}
3、混淆配置的语法
如果你能把上面proguard-android.txt说明看完,基本就可以照着写出自己的混淆文件,如果需要了解更多可参考文章:ProGuard 最全混淆规则说明。
4、不能混淆代码说明
四大组件和Application:Activity、service等和Application不能混淆。现在的系统已经配置为混淆时候会保留Android系统组件;
使用反射的类:因为代码混淆,类名、方法名、属性名都改变了,而反射它还是按照原来的名字去反射,结果程序崩溃。
注解:因为注解也用到了java反射,所以不能混淆。
自定义View:因为被Android Resource 文件引用到的,名字已经固定,也不能混淆。自定义view是带了包名写在xml布局中的。
JavaBean 类:使用了 Gson 之类的工具要使 JavaBean 类即实体类不能混淆。因为json转换用到了java反射。
泛型:泛型不能混淆。
自定义控件类:控件类的get/set方法和构造函数不能混淆。
内部类:如果内部类会被外部调用到,那么也不能混淆。
使用了枚举的类:使用了枚举要保证枚举不被混淆。
第三方库类:对第三方库中的类不进行混淆。
包含native方法类:不混淆任何包含native方法的,否则找不到本地方法。
属性动画兼容库:属性动画兼容库不能混淆。
在引用第三方库的时候,一般会标明库的混淆规则的,建议在使用的时候就把混淆规则添加上去,免得到最后才去找。
不混淆Rxjava/RxAndroid。
数据库驱动。
5、混淆后调试
在Android studio 中生成release包的同时 build\outputs\mapping\release文件夹下也生成了4个文件:
configuration.txt :总的混淆规则。这个文件包含了打包过程中所有混淆规则的汇总。
mapping.txt :列出了原始的类,方法,和字段名与混淆后代码之间的映射。
seeds.txt :列出了未被混淆的类和成员。
usage.txt : 列出了从apk中删除的代码。
根据上面文件信息,反推出原代码逻辑问题。
使用retrace工具
该工具在sdk根目录\tools\proguard\bin\retrace.sh,使用步骤:
保存crash日志,转存到bug.txt中;
删掉Crash日志中AndroidRuntime前面的信息;
打开命令窗口执行命令:如: ./retrace.sh mapping.txt bug.txt