四、proguard配置示例
4.1 Android默认推荐配置
在IDE自动生成的project.properties文件中,有这样一行:
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
Android Studio默认生成的build.gradle文件有如下配置:
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
其中getDefaultProguardFile('proguard-android.txt')获取的也是tools/proguard/proguard-android.txt。下面看一下这个文件的配置:
# 不使用大小写混合类名 -dontusemixedcaseclassnames # 不路过引用库中的非public类 -dontskipnonpubliclibraryclasses # 输出更多信息 -verbose # 不进行优化 -dontoptimize # 不进行预校验 -dontpreverify # keep注解 -keepattributes *Annotation* #keep google license服务接口 -keep public class com.google.vending.licensing.ILicensingService -keep public class com.android.vending.licensing.ILicensingService # keep native方法及其所属类 -keepclasseswithmembernames class * { native <methods>; } # keep自定义view的get/set方法 -keepclassmembers public class * extends android.view.View { void set*(***); *** get*(); } # keep继续自Activity中所有包含public void *(android.view.View)签名的方法,如onClick -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } # keep枚举中的values和valueOf方法 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # keep Parcelable的CREATOR成员 -keepclassmembers class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator CREATOR; } # keep R文件的静态字段 -keepclassmembers class **.R$* { public static <fields>; } # 不输出support包中的警告 -dontwarn android.support.** 4.2 一个典型library库的配置 示例引用自官方文档samples # 保存mapping映射文件到out.map -printmapping out.map # keep已keep方法的参数类型及参数名称 -keepparameternames # 这个配置未弄清楚,待测试 -renamesourcefileattribute SourceFile -keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod # keep所有类的protected成员 -keep public class * { public protected *; } # keep在jdk 1.2中编译器插入的代码 -keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); } # keep native方法 -keepclasseswithmembernames,includedescriptorclasses class * { native <methods>; } # keep枚举中的values和valueOf方法 -keepclassmembers,allowoptimization enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # keep系列化相关方法 -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(); }
4.3 一个典型Android App的配置
示例引用自官方文档samples
-dontpreverify -repackageclasses '' -allowaccessmodification # 不优化算法指令 -optimizations !code/simplification/arithmetic -keepattributes *Annotation* # keep继承自系统组件的类 -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider # keep自定义view及其构造方法、set方法 -keep public class * extends android.view.View { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet, int); } -keepclassmembers class * extends android.content.Context { public void *(android.view.View); public void *(android.view.MenuItem); } -keepclassmembers class * implements android.os.Parcelable { static ** CREATOR; } -keepclassmembers class **.R$* { public static <fields>; } # keep javascript注释的方法,使用到webview js回调方法的需要添加此配置 -keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; }
五、关于反射
并不是所有会被反射引用的类都必须keep,在progurad过程中能直接分析到引用的类会被proguard做相应的处理:
# Class.forName的类名"SomeClass"被混淆后自动替换 Class.forName("SomeClass") SomeClass.class # 以下字段和方法名都会在被混淆后自动替换 SomeClass.class.getField("someField") SomeClass.class.getDeclaredField("someField") SomeClass.class.getMethod("someMethod", new Class[] {}) SomeClass.class.getMethod("someMethod", new Class[] { A.class }) SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class }) SomeClass.class.getDeclaredMethod("someMethod", new Class[] {}) SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class }) SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class }) AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField") AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField") AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")
写个demo验证下:
Class<?> clazz = Class.forName("com.rush.test.SimpleClass1"); clazz.getDeclaredMethod("Test1"); SimpleClass2.class.getDeclaredField("mTestField"); SimpleClass2.class.getDeclaredMethod("Test2");
对以上代码编译并proguard,结果如下:
Class.forName("com.rush.a.a").getDeclaredMethod("Test1", new Class[0]); b.class.getDeclaredField("a"); b.class.getDeclaredMethod("a", new Class[0]);
通过Class.forName反射的class com.rush.test.SimpleClass1"被自动替换成了"com.rush.a.a";
但通过Class.forName获取的class再去反射方法没有正确处理;
通过完整class.getDeclaredField或者getDeclaredMethod反射时能够把字段名和方法名自动替换掉。
从结果看,反射并不是大家想像的那样必须keep,proguard能自动分析到引用的情况都能正确处理。但有些类是在配置文件里配置,或者动态拼接类名反射的,这些情况需要做好keep。
为了问题追踪的方便,建议所有会被反射引用的代码和library public接口都做好keep。
六、关于proguard配置的一些建议
所有会被反射引用的类都做好keep(建议,虽然有些反射能被正确处理)。
如native方法,四大组件,接口model,枚举,序列化类等。
只keep必须保留的内容,不要过度keep
使用热修复的App,添加-dontoptimize配置
七、参考文档:
https://www.guardsquare.com/en/proguard/manual/introduction