# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
# 混淆时不使用大小写混合类名
-dontusemixedcaseclassnames
# 不跳过library中的非public的类
-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>(...);
}
-
首先看
keep
类关键字:
关键字 | 含义 |
---|---|
keep | 保留类和类成员,防止被混淆或移除 |
keepnames | 保留类和类成员,防止被混淆,但没有被引用的类成员会被移除 |
keepclassmembers | 只保留类成员,防止被混淆或移除 |
keepclassmembernames | 只保留类成员,防止被混淆,但没有被引用的成员会被移除 |
keepclasseswithmembers | 保留类和类成员,防止被混淆或移除,如果指定的类成员不存在还是会被混淆 |
keepclasseswithmembernames | 保留类和类成员,防止被混淆,如果指定的类成员不存在还是会被混淆,没有被引用的类成员会被移除 |
-
相关通配符:
通配符 | 含义 |
---|---|
* | 匹配任意长度字符,但不含包名分隔符. 。例如一个类的全包名路径是com.othershe.test.Person ,使用com.othershe.test.* 、com.othershe.test.* 都是可以匹配的,但com.othershe.* 就不能匹配 |
** | 匹配任意长度字符,并包含包名分隔符. 。例如要匹配com.othershe.test.** 包下的所有内容 |
* | 匹配任意参数类型。例如*** getName(***) 可匹配String getName(String) |
... | 匹配任意长度的任意类型参数。例如void setName(...) 可匹配void setName(String firstName, String secondName) |
<fileds> | 匹配类、接口中所有字段 |
<methods> | 匹配类、接口中所有方法 |
<init> | 匹配类中所有构造函数 |
到这里对混淆已经有了基本的了解,系统的proguard-android.txt
已经为我们完成了大部分基础的混淆配置工作,至于编写当前app module
下的proguard-rules.pro
,只需要针对当前项目添加一些特有的配置,避免某些重要的东西被混淆掉导致错误,我们主要考虑以下几点:
-
在
AndroidManifest.xml
中注册的继承四大组件的子类的类名以及重写的方法名都不会被混淆。
如果希望项目中android.support.v4.app.Fragment
子类的类名和重写父类的方法名不被混淆可以添加如下配置:
# 不混淆Fragment的子类类名以及onCreate()、onCreateView()方法名
-keep public class * extends android.support.v4.app.Fragment {
public void onCreate(android.os.Bundle);
public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
}
-
不混淆某个特定的类和类中所有成员
-keep class com.othershe.test.utils.CommonUtil { *; }
-
不混淆某个目录下的文件,例如使用
Gson
时,数据bean不能被混淆,需要如下配置:
# com.othershe.test.model代表数据bean所在的全包名目录
-keep class com.othershe.test.model.** { *; }
-
上一条的具体原因是因为
Gson
用到了反射。如果我们自己使用了反射,例如
Field field = service.getField("BASE_URL");
BASE_URL
是service所属类的一个字段名,则该字段不能被混淆。
-
保留泛型
-keepattributes Signature
-
保留用于调试堆栈跟踪的行号信息(为了后期调试方便,建议配置)
-keepattributes SourceFile,LineNumberTable
如果使用了上一行配置,还需要添加如下配置将源文件重命名为SourceFile,以便通过鼠标点击直达源文件:
-renamesourcefileattribute SourceFile
-
WebView
中使用了JS
调用,需要添加如下配置:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-
项目中使用的第三方
library
混淆规则,列举了几个常用的:
# okhttp
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
# Retrofit
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontnote retrofit2.Platform
-dontwarn retrofit2.Platform$Java8
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
# Gson
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
# xxx代表model类的全包名路径
-keep class xxx.** { *; }
# butterknie
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
# eventbus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
以上这些可以按需添加到proguard-rules.pro
!
查看混淆结果
混淆后打包,会在app module/build/outputs/mapping/release
目录下生成如下文件(动不动就几万行,是在没法看):
-
dump.txt
:描述apk
文件中所有类的内部结构 -
mapping.txt
:混淆前后的类、类成员、方法的对照关系(重要,追溯Crash堆栈信息要用到) -
resources.txt
:资源文件的压缩信息 -
seeds.txt
:未被混淆的类和成员 -
usage.txt
:被移除的代码