@TryLoveCatch
2021-07-08T09:34:55.000000Z
字数 9106
阅读 1583
android android基础 Proguard
https://www.jianshu.com/p/86ee6ef970ef
https://www.jianshu.com/p/b471db6a01af
https://cloud.tencent.com/developer/article/1334693
https://juejin.im/post/5aeae5b6f265da0ba469a265
https://www.cnblogs.com/cr330326/p/5534915.html
https://sivanliu.github.io/2017/10/21/%E6%B7%B7%E6%B7%86/
https://www.jianshu.com/p/1b76e4c10495
https://juejin.im/post/5c97485f6fb9a070f30b0af7
https://www.jianshu.com/p/c54d7b35806c
https://juejin.im/post/5c97485f6fb9a070f30b0af7
https://www.cnblogs.com/cr330326/p/5534915.html
Android SDK 自带了混淆工具Proguard。它位于SDK根目录\tools\proguard下面。如果开启了混淆,Proguard默认情况下会对所有代码,包括第三方包都进行混淆,可是有些代码或者第三方包是不能混淆的,这就需要我们手动编写混淆规则来保持不能被混淆的部分。
混淆有4个作用:

我们刚才应该也看到了 ProGuard 压缩阶段是从代码入口点开始递归查找用到的代码的。
ProGuard 怎么知道哪里是代码入口点的呢? 没错这个代码入口点如果我们不告诉 ProGuard,他是不会知道的。那么怎么告诉他呢?我们通过 keep 规则就可以告诉 ProGuard 了。
下面看个例子:
ModelA.java {int testA = 2;public void modelA(int age) {int a = 1 + age;int b = testA + age;System.out.println("print " + b);}public void modelB(String name) {System.out.println("print " + name);}}ModelB.java {public void modelA(String name) {System.out.println("print " + name);}public void modelB(String name) {System.out.println("print " + name);}}UtilsA.java {private static final String UtilA = "utila";public static void printA() {System.out.println("print " + UtilA);}public static void printB() {System.out.println("print B");}}Main.java {public static Main sMain = null;public static void main(String[] args) {sMain = new Main();sMain.run();}private void run() {ModelA modelA = new ModelA();modelA.modelA(5);UtilsA.printA();}}
可以看出来,这个main函数里面只用到了ModelA和UtilsA,我们先不添加任何混淆参数,混淆之后的结果:
a.java {private int a = 2;public final void a(int i) {System.out.println("print " + (this.a + 5));}}Main.java {private static Main a = null;public static void main(String[] strArr) {a = new Main();new a().a(5);System.out.println("print utila");}}
a.java是ModelA,对比一下混淆前和混淆后的 Jar 包内容:
UtilsA.printA();合并了,直接调用了打印语句为什么 Main 这个类以及 main 方法没有被混淆呢?
在 ProGuard 默认生成的配置文件下有个条匹配规则(这是javar的不是android的):
-keepclasseswithmembers public class * {public static void main(java.lang.String[]);}
匹配每个类里面的 main 方法为代码入口点
Class Specification是指一个类和类成员的模板。它一般跟在各种-keep配置或者assumenosideeffects配置之后,只有匹配到的类和类成员会受到影响。

上面这几个功能都是默认打开的,要关闭他们只需配置规则:
官网
#关闭压缩-dontshrink#关闭优化-dontoptimize#迭代优化,n表示proguard对代码进行迭代优化的次数,Android一般为5-optimizationpasses n#关闭混淆 默认开启,保持默认即可。-dontobfuscate# 不做预校验,preverify是proguard的4个步骤之一# Android不需要preverify,去掉这一步可加快混淆速度-dontpreverify
主要有两个功能
举个例子来验证一下这两点:
DownloadClient.java{public int status = 0;public String url;private HttpDownload httpDownload;public DownloadClient(String url) {this.url = url;httpDownload = new HttpDownload();}public void start() {status = 1;httpDownload.start();}public void stop() {status = 2;httpDownload.stop();}}DownloadManager.java{HttpRequest httpRequest = new HttpRequest();public HttpRequest getDownloadUrl() {System.out.println(httpRequest.get());return httpRequest;}}HttpDownload.java{private int i=0;public void start(){System.out.println("开始下载");i++;}public void stop(){System.out.println("停止下载");i--;}}HttpRequest.java{public String get() {return "请求成功";}}
增加keep语句:
-keep class DownloadClient {public java.lang.String url;public <init>(java.lang.String);public void start();}
混淆效果如下:
a.java{private int a = 0;public final void a() {System.out.println("开始下载");this.a++;}}DownloadClient.java{private int a = 0;private a b;public String url;public DownloadClient(String str) {this.url = str;this.b = new a();}public void start() {this.a = 1;this.b.a();}}
我们对比一下:
https://juejin.im/post/5c97485ff265da6120099458
过滤器是什么呢?就是类似正则的规则过滤器。
| 符号 | 功能 | 示例 |
|---|---|---|
| ? | 可以匹配任意一个字符,但是 package 的分隔符(.)除外 | com.example.T?st 可以匹配 com.example.Test,com.example.T2st 不能匹配com.example.T12st 、com.example.Tst 和 com.example.T.st |
| * | 可以匹配任意一部分连续的字符,但是 package 的分隔符(.)除外 | com.example.*Test可以匹配 com.example.Test,com.example.AnyTest com.example.xxx.Test 还有一个特例 com.example.* 只能匹配当前包下的类,不到匹配com.example.xxx.Test |
| ** | 可以匹配任意一部分连续的字符 | com**可以匹配 com.Test,com.example.Test,com.example.java.Test |
| <n> | 在同一匹配规则中匹配和第 n 个通配符一致的内容。 | *Any<1> 可以匹配TestAnyTest 不能匹配TestAnytest。 |
| 符号 | 功能 | 示例 |
|---|---|---|
| <init> | 匹配所有构造方法 | |
| <fields> | 匹配所有字段方法 | |
| <methods> | 匹配所有方法 | |
| * | 匹配所有方法和字段,包括构造方法。 | |
| ? | 匹配方法名称中的任何单个字符。 | |
| <n> | 在同一匹配规则中匹配和第 n 个通配符一致的内容。 |
-keep class DownloadClient {<fields>;}
-keep class DownloadClient {<methods>;}
| 符号 | 功能 | 示例 |
|---|---|---|
| % | 匹配所有基本类型(int, boolean, long, float,double等) | |
| ? | 匹配类名中的任何单个字符 | |
| * | 匹配不包含包分隔符的类名的任何部分。 | |
| ** | 匹配类名的任何部分,可能包含任意数量的包分隔符。 | |
| 匹配所有任何类型,包括初始类型和非初始类型,数组和非数组。 | ||
| ... | 匹配任何类型任何数量的参数 | |
| <n> | 在同一匹配规则中匹配和第 n 个通配符一致的内容。 |
?,* 和 ** 通配符不可以匹配原始类型和数组,比如:int,float。包装类和普通类是可以匹配的,比如:Integer,String。
| 符号 | 功能 | 示例 |
|---|
所有 Event 结尾的类里面的所有内容都不能混淆。
-keep class **Event { *; }
所有 XXX 子类中的 x() 方法不混淆。
-keep class ** extends XXX {void x();}
但是 XXX 类被混淆了,如果不想 XXX 被混淆,那就在另写一条匹配规则。
https://www.jianshu.com/p/1b76e4c10495
ANDROID_SDK\tools\proguard\proguard-android.txt
#混淆时不生成大小写混合的类名-dontusemixedcaseclassnames#不忽略非公共的类库-dontskipnonpubliclibraryclasses#混淆过程中打印详细信息-verbose#关闭优化-dontoptimize#不预校验-dontpreverify# Annotation注释不能混淆-keepattributes *Annotation*#对于NDK开发 本地的native方法不能被混淆-keepclasseswithmembernames class * {native <methods>;}#保持View的子类里面的set、get方法不被混淆(*代替任意字符)-keepclassmembers public class * extends android.view.View {void set*(***);*** get*();}#保持Activity子类里面的参数类型为View的方法不被混淆,如被XML里面应用的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);}#保持枚举类型values()、以及valueOf(java.lang.String)成员不被混淆-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);}#保持实现Parcelable接口的类里面的Creator成员不被混淆-keepclassmembers class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator CREATOR;}#保持R类静态成员不被混淆-keepclassmembers class **.R$* {public static <fields>;}#不警告support包中不使用的引用-dontwarn android.support.**-keep class android.support.annotation.Keep-keep @android.support.annotation.Keep class * {*;}#保持使用了Keep注解的方法以及类不被混淆-keepclasseswithmembers class * {@android.support.annotation.Keep <methods>;}#保持使用了Keep注解的成员域以及类不被混淆-keepclasseswithmembers class * {@android.support.annotation.Keep <fields>;}-keepclasseswithmembers class * {@android.support.annotation.Keep <init>(...);}
你想进一步压缩代码就可以改使用 getDefaultProguardFile("proguard-android-optimize.txt") ,但是会更加耗时。
ANDROID_SDK\tools\proguard\proguard-android-optimize.txt
// 删除了关闭优化指令# -dontoptimize// 添加以下规则-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*-optimizationpasses 5-allowaccessmodification-dontpreverify
https://www.jianshu.com/p/1b76e4c10495
https://www.jianshu.com/p/86ee6ef970ef
sdk里面如果minifyEnabled为true的话,app使用的时候 会混淆吗?
应该有两种方式,可以把sdk的给带到app里面
https://www.jianshu.com/p/9dacabd351e3
因为默认的\sdk\tools\proguard\proguard-android.txt里面就已经对Keep的混淆,而且默认的在类上添加Keep会不混淆整个类
https://www.jianshu.com/p/a8614ff60647
一般情况,我们都是使用proguardFiles,如下所示:
defaultConfig {minSdkVersion 16targetSdkVersion 26versionCode 2versionName "2.0"minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}
当然,也可以放到具体的buildTypes下面:
buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}
但是这些对sdk来说,其实都是不起作用的,这个时候,我们需要使用gradle的consumerProguardFiles,来实现sdk的混淆。
consumerProguardFiles的使用位置和proguardFiles是一样的
consumerProguardFiles 'proguard.pro'consumerProguardFiles 'proguard-a.pro','proguard-b.pro' consumerProguardFiles fileTree(dir: projectDir, include: 'proguard*')sdk里面如果minifyEnabled为true的话,app使用的时候 会混淆吗?
现在我们让我们来回答这个的问题:
我们假设有三个module,如下
注意:我们把consumerProguardFiles没有放到了defaultConfig
好了,我们来看一下结论:
所以说,结论如下: