ProGuard入门
简单过程
shrinker
:检测和移除无用的类、方法、变量和属性
optimizer
:优化代码,无用参数会被移除,一些方法会变成内联代码,非入口节点类会加上private/static/final
obfuscator
:使用混淆字典对非入口类的类名、变量名、方法名进行重命名
preverifier
:预校验代码是否符合java规范
配置
文艺配置: 官方默认配置,只在relase阶段打开混淆开关,也就是我们的打正式前签名包的时候开启混淆。但是如果项目比较大有许多Module Library,同时我们进行debug run的时候这样配置会让这些Library被开启混淆,因为gradle在lib构建时最后一步是 task:bundleRelease也就是说此时是buildTypes是release
在应用的build.gradle
中打开混淆开关
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}
getDefaultProguardFile('proguard-android.txt') 方法从 Android SDK tools/proguard/ 文件夹获取默认的 ProGuard 设置。
也可以使用进阶版proguard-android-optimize.txt 它包括相同的Android混淆规则而且还包括字节码一级(方法内和方法间)执行分析的优化,以进一步减小APK大小和帮助提高其运行速度。
proguard-rules.pro文件用于添加自定义 ProGuard 规则。默认情况下,该文件位于模块根目录(build.gradle 文件旁)
文艺配置: 抽出上面的配置信息新建一个configProgurad.gradle
,通过获取Run执行时候task的name来动态的添加apply from:yourpath/configProgurad.gradle
是否加上上面的混淆配置。例如debug run是gradlew :app.xx:assembleDebug
判断assembleDebug
这时我们就不进行apply添加。
产出物
每次proguard都会有对应的文件产物在build/outputs/mapping/release/
下
dump.txt
说明 APK 中所有类文件的内部结构。
mapping.txt
提供原始与混淆过的类、方法和字段名称之间的转换。注意它每次编译都会被覆盖,所以每次发包都应该保持一份。第三方错误收集SDK网站一般会提供mapping文件上传已便对混淆代码中bug快速定位。
seeds.txt
列出未进行混淆的类和成员。
usage.txt
列出从 APK 移除的代码。
Keep
代码
语法
命令 | 作用 |
---|---|
-keep | 防止类和成员被移除或者被重命名 |
-keepnames | 防止类和成员被重命名 |
-keepclassmembers | 防止成员被移除或者被重命名 |
-keepclassmembernames | 防止成员被重命名 |
-keepclasseswithmembers | 防止拥有该成员的类和成员被移除或者被重命名 |
-keepclasseswithmembernames | 防止拥有该成员的类和成员被重命名 |
[keep命令] [类] {
[成员]
}
“类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:
- 具体的类
- 访问修饰符(
public
、protected
、private
) - 通配符
*
,匹配任意长度字符,但不含包名分隔符(.) - 通配符
**
,匹配任意长度字符,并且包含包名分隔符(.) -
extends
,即可以指定类的基类 -
implement
,匹配实现了某接口的类 -
$
,内部类
“成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:
- 匹配所有构造器
- 匹配所有域
- 匹配所有方法
- 通配符
*
,匹配任意长度字符,但不含包名分隔符(.) - 通配符
**
,匹配任意长度字符,并且包含包名分隔符(.) - 通配符
***
,匹配任意参数类型 -
…
,匹配任意长度的任意类型参数。比如void test(…)
就能匹配任意void test(String a)
或者是void test(int a, String b)
这些方法。 - 访问修饰符(
public
、protected
、private
)
举个例子,假如需要将com.android.app
包下所有继承Activity
的public
类及其构造函数都保持住,可以这样写:
-keep public class com.android.app.** extends Android.app.Activity {
<init>
}
最新的Android SDK tool中的官方proguard文件已经覆盖了几乎全部有关Android相关的混淆keep,比如四大组件和view等。但是涉及到下面几种情况需要手动添加keep防止类被混淆或者移除:
- JNI接口方法
- 反射的类和方法
- Android中Manifest中配置的类
- 用到的第三方的jar包,一般规范的第三方jar提供者一般已经做了混淆而且代码也遵循混淆规则
- 特别处理js与本地原生组件之间的调用方法
- 使用了json解析的Bean类
- 通过api版本不同调用继承的过时方法,比如WebChromeClient类中的openFileChooser
资源
shrinkResources
依赖于minifyEnabled
选项,只有经过代码压缩器移除无用代码后,才可以判断哪些资源是真正没有别使用而可以被移除。
AAPT限制不允许Gradle为资源指定预定义版本,
values/
中的资源不会被移除包括字符串、尺寸、样式和颜色。
想要keep资源可以创建一个xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
tools:discard="@layout/unused2" />
当代码中使用了Resource.getIdentifer()
方法或者一些第三方库使用了这个方法
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
资源压缩器会把所有匹配格式img
的资源标记为已经使用无法移除。可以在keep.xml
中设置shrinkMode
为strick
启动严格模块,来关闭这个匹配查询。
混淆后堆栈查看
使用 retrace 脚本(在 Windows 上为retrace.bat
;在 Mac/Linux 上为retrace.sh
)。它位于 <sdk-root>/tools/proguard/
目录中,官方使用:
retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
本地发布的AAR
项目中有本地的Library发布到远程变成AAR引用的时候,需要添加如下配置确保当引用者开启混淆时AAR会根据自己的混淆规则进行混淆。
defaultConfig {
consumerProguardFiles 'proguard-rules.pro'
}