代码混淆的作用
1.令 APK 难以被逆向工程,即很大程度上增加反编译的成本; 2.在打包时移除无用资源,显著减少 APK 体积; 3.对字节码进行优化,移除无用指令,应用运行更快; 4.以变通方式避免 Android 中常见的 64k 方法数引用的限制。
设置混淆
1. 在模块目录下的 build.gradle 文件中配置以下代码
buildTypes { release { minifyEnabled true // 开启混淆 shrinkResources true // 优化无用资源 } }
2. 在 proguard-rules.pro 文件中添加混淆规则
通用混淆规则
-optimizationpasses 5 # 代码混淆的压缩比例,值介于0-7,默认5 -verbose # 混淆时记录日志 -dontoptimize # 不优化输入的类文件 -dontshrink # 关闭压缩 -dontpreverify # 关闭预校验(作用于Java平台,Android不需要,去掉可加快混淆) -dontoptimize # 关闭代码优化 -dontobfuscate # 关闭混淆 -ignorewarnings # 忽略警告 -dontwarn com.squareup.okhttp.** # 指定类不输出警告信息 -dontusemixedcaseclassnames # 混淆后类型都为小写 -dontskipnonpubliclibraryclasses # 不跳过非公共的库的类 -printmapping mapping.txt # 生成原类名与混淆后类名的映射文件mapping.txt -useuniqueclassmembernames # 把混淆类中的方法名也混淆 -allowaccessmodification # 优化时允许访问并修改有修饰符的类及类的成员 -renamesourcefileattribute SourceFile # 将源码中有意义的类名转换成SourceFile,用于混淆具体崩溃代码 -keepattributes SourceFile,LineNumberTable # 保留行号 -keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod # 避免混淆注解、内部类、泛型、匿名类 -optimizations !code/simplification/cast,!field/ ,!class/merging/ # 指定混淆时采用的算法
常用匹配符
# * 星号 匹配指定包下的类或者类的子类: -keep class top.gaojc.model.* -keep class top.gaojc.model.** # 释义: 1. 一个*号表示只保持该包(top.gaojc.model)下的类名不被混淆,而子包下的类名还是会混淆,比如model下面有个bean子包,它里面的类会被混淆 2. 两个*号表示保持该包(top.gaojc.model)下及该包下子包的包名不被混淆 3. 如果既想保持类名,又想保持方法和成员变量不被混淆,需要使用以下方式: -keep class top.gaojc.model.*{*;} <init> 匹配类中所有构造函数 <methods> 匹配类、接口中所有方法 <fileds> 匹配类、接口中所有字段
常用命令
# 保留类和类成员,防止类和类成员被移除或被混淆; -keep # 保留类和类成员,防止被混淆,但没有被引用的类成员会被移除 -keepnames # 只保留类成员,防止类成员被移除或被混淆; -keepclassmembers # 只保留类成员,防止被混淆,但没有被引用的成员会被移除 -keepclassmembernames # 保留类和类成员,防止被混淆或移除,如果指定的类成员不存在还是会被混淆 -keepclasseswithmembers # 保留类和类成员,防止被混淆,如果指定的类成员不存在还是会被混淆,没有被引用的类成员会被移除 -keepclasseswithmembernames # 不混淆某个类的类名及类中的内容 -keep class top.gaojc.gameapp.debug.DebugActivity { *; } # 不混淆指定包名下的类名,不包括子包下的类名 -keep class top.gaojc.gameapp* # 不混淆指定包名下的类名,及类里的内容 -keep class top.gaojc.gameapp* {*;} # 不混淆指定包名下的类名,包括子包下的类名 -keep class top.gaojc.gameapp** # 不混淆某个类的子类 -keep public class * extends top.gaojc.gameapp.base.BaseActivity # 不混淆实现了某个接口的类 -keep class * implements top.gaojc.gameapp.dao.ChatDaiImpl # 不混淆类名中包含了"entity"的类,及类中内容 -keep class **.*entity*.** {*;} # 不混淆内部类中的所有public内容 -keep class top.gaojc.gameapp.view.CustomView$OnClickInterface { public *; } # 不混淆指定类的所有方法 -keep top.gaojc.gameapp.debug.DebugActivity { public <methods>; } # 不混淆指定类的所有字段 -keep top.gaojc.gameapp.debug.DebugActivity { public <fields>; } # 不混淆指定类的所有构造方法 -keep top.gaojc.gameapp.debug.DebugActivity { public <init>; } # 不混淆指定参数作为形参的方法 -keep top.gaojc.gameapp.debug.DebugActivity { public <methods>(java.lang.String); } # 不混淆类的特定方法 -keep top.gaojc.gameapp.debug.DebugActivity { public test(java.lang.String); } # 不混淆native方法 -keepclasseswithmembernames class * { native <methods>; } # 不混淆枚举类 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } #不混淆资源类 -keepclassmembers class **.R$* { public static <fields>; } # 不混淆自定义控件 -keep public class * entends android.view.View { *** get*(); void set*(***); public <init>; } # 不混淆实现了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(); } # 不混淆实现了parcelable接口的类成员 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } # 保留
注意事项
-. jni方法不可混淆,方法名需与native方法保持一致 -keepclasseswithmembernames class * { native <methods>; } -. 反射用到的类不混淆,否则反射可能出问题 -. 四大组件、Application子类、Framework层下的类、自定义的View默认不会被混淆,无需另外配置 -. WebView的JS调用接口方法不可混淆 -. 注解相关的类不混淆 -. GSON、Fastjson等解析的Bean数据类不可混淆 -. 与服务端交互解析成的实体对象不能设置混淆,否则不能解析出正确的对象 -keep class top.gaojc.model.bean.*{*;} # 自定义数据模型的bean目录 -. 枚举enum类中的values和valuesof这两个方法不可混淆(反射调用) -. 继承Parceable和Serializable等可序列化的类不可混淆 -. 第三方库或SDK,请参考第三方提供的混淆规则,没提供的话,建议第三方包全部不混淆
如何查看是否已混淆
在 Android Studio 的 Build 菜单下点击 Analyze APK 选项,选择生成的apk包,然后点击 classes.dex 文件即可看到混淆后的代码结构