Android中代码混淆:ProGuard 的官网 https://www.guardsquare.com/en/products/proguard#manual/usage.html
参考自http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2018/0409/9568.html
https://zhuanlan.zhihu.com/p/92803018
https://www.jianshu.com/p/60e82aafcfd0/
Android studio中开启混淆
buildTypes {
release {
minifyEnabled true //开启混淆
zipAlignEnabled true //压缩优化
shrinkResources true //移出无用资源
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //默认的混淆文件以及我们指定的混淆文件
}
}
一、Android混淆的原则
1、反射用到的类不混淆
2、JNI方法不混淆
3、AndroidMainfest中的类不混淆,四大组件和Application的子类和Framework层下所有的类默认不会进行混淆
4、Parcelable的子类和Creator静态成员变量不混淆,否则会产生android.os.BadParcelableException异常
5、使用GSON、fastjson等框架时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象
6、使用第三方开源库或者引用其他第三方的SDK包时,需要在混淆文件中加入对应的混淆规则
7、有用到WEBView的JS调用也需要保证写的接口方法不混淆
8、自定义控件,枚举类,JavaBean 不用混淆
2、google默认混淆文件位置:
\sdk\tools\proguard\proguard-android.txt
\sdk\tools\proguard\proguard-android-optimize.txt ;
默认混淆文件说明:
从上图比较来看,在 proguard-android-optimize.txt 设置了压缩级别和压缩算法。所以如果你需要优化就用 proguard-android-optimize.txt这个,不需要优化就用 proguard-android.txt这个。
3、Android多模块混淆的问题
Android在多模块或者组件化的时候,关于混淆的管理,一般常见的做法就是两条。
- 把所有的混淆规则规则都放在
app
模块下面,由app
统一管理。这样就会有一个问题,就是到会导致混淆规则的冗余。 - 由
module
管理自己的混淆规则,这样的话需要你对自己的模块有一个很好的管理。
这里就是记录下,由module
的处理混淆的方法,参看官方文档。管理子module的方法,本质上就是管理aar的方法,是通用的。在module中添加:
release {
consumerProguardFiles 'proguard-rules.pro'
}
这样就可以了,需要注意的是,
- 多模块或者组件化混淆,只要app模块开了混淆,子模块无论是否打开混淆都是默认开启的。只是通过上面的方法,子模块可以自定义混淆的规则。
- 子模块的混淆规则是无法影响app模块的的。所以建议,在子模块里尽量只放和子模块相关的混淆规则,一些公有的混淆方式请放在app或者公有的模块中。
4、混淆常用操作说明
1、在proguard-android-optimize.txt已经有的属性:
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* (混淆时所采用的算法)
-optimizationpasses 5 #指定代码压缩级别 (设置代码的压缩比率 0~7,Android一般设置为5)
-allowaccessmodification #这项配置是设置是否允许改变作用域的。使用这项配置之后可以提高优化的效果。**但是,如果你的 代码是一个库的话,最好不要配置这个选项,因为它可能会导致一些
private
变量被改成public
。-dontpreverify #不预校验
-dontskipnonpubliclibraryclasses #指定读取引用库文件的时候不跳过非public类(与-skipnonpubliclibraryclasses相对应)
-verbose #混淆时记录日志
-keepattributes *Annotation* 保持注解不混淆
-keep public class com.google.vending.licensing.ILicensingService (保持类名不混淆)
-keep public class com.android.vending.licensing.ILicensingService (保持类名不混淆)
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#--保留自定义控件(继承自View)不被混淆-(保持该类下的特定方法不被混淆,其他方法和类名会被混淆)
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}#--原理同上 -keepclassmembers 保持继承自Activity的类的特定方法不被混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# 保持枚举 enum 类的特定方法不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}保留 Parcelable 序列化类的特定方法不被混淆--
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}# 不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。
-keepclassmembers class **.R$* {
public static <fields>;
}
#保持类名不混淆
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
#-keepclasseswithmembers 不需要保持类名,只需要保持该类下的特定方法保持不被混淆
-keepclasseswithmembers class * {
@android.support.annotation.Keep <methods>;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep <init>(...);
}
2、需要自定义的部分:
如果默认使用proguard-android-optimize.txt混淆文件的话,在自定义混淆文件proguard-rules.pro这个里面就不需要
添加上面的内容了,否则需要把上面的内容添加进来。
proguard-rules.pro文件内容:
#------------------------------基本不用动区域----------------------------------------------------------
-ignorewarnings#忽略警告(否则打包可能会不成功)
-dontusemixedcaseclassnames#指定在混淆时不生成混合大小写的类名
-keepattributes Signature#避免混淆泛型 如果混淆报错建议关掉
-keepattributes EnclosingMethod#用到了反射需要加入-
-keepattributes SourceFile,LineNumberTable# 抛出异常时保留代码行号,保持源文件以及行号#-----------------------------默认保留区----------------------- #---------不需要混淆系统组件 -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 public class * extends android.preference.Preference -keep public class * extends android.view.View -keep public class * extends android.app.backup.BackupAgentHelper -keep public class com.android.vending.licensing.ILicensingService
# 保留继承的 -keep public class * extends android.support.v4.** -keep public class * extends android.support.v7.** -keep public class * extends android.support.annotation.** -keep public class * extends android.app.Fragment
# 保持自定义控件类的特定方法不被混淆 -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.app.Activity { public void *(android.view.View); }
#保持 Serializable 不被混淆 -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient <fields>; !private <fields>; !private <methods>; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆 -keepclassmembers class * { void *(**On*Event); void *(**On*Listener); }
#对于带有回调函数的 onXXEvent 的不能被混淆------ -keepclassmembers class * { void *(**On*Event); } #-WebView,没有使用 WebView 请注释掉------ -keepclassmembers class fqcn.of.javascript.interface.for.webview { public *; } -keepclassmembers class * extends android.webkit.webViewClient { public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); public boolean *(android.webkit.WebView, java.lang.String); } -keepclassmembers class * extends android.webkit.webViewClient { public void *(android.webkit.webView, jav.lang.String); }
# 保护注解 -keep class * extends java.lang.annotation.Annotation {*;}
# 不混淆内部类 -keepattributes InnerClasses
#----------------------------变化区域--------------------------------------------------
#-----------处理反射类---------------
#-----------处理js交互---- ----------- #--保持该类下的特定方法不被混淆 -keepclassmembers class com.handscape.fun.SanitationJs{ public *; }
#-----------处理实体类--------------- # 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。 -keep class com.handscape.bean.** { *; }
#-----------------第三方包的混淆-----
#--第三方不混淆 通常采用 -dontwarn和-keep 祝贺使用
例如:
#-------避免混淆Bugly-------------------- #--不警告该包和该包和该包的子包下的类 -dontwarn com.tencent.bugly.** #-不混淆该包和该包的子包下的内容 -keep public class com.tencent.bugly.**{*;}
5、说明:
<init>; //匹配所有构造器
<fields>; //匹配所有域
<methods>; //匹配所有方法方法
# -keep关键字
# keep:包留类名和类中的方法,防止他们被混淆
# keepnames:保留类名和类中的方法防止被混淆,但方法如果没有被引用将被删除
# keepclassmembers :只保留类中的方法,防止被混淆和移除。
# keepclassmembernames:只保留类中的方法,但如果方法没有被引用将被删除。
# keepclasseswithmembers:如果当前类中包含指定的方法,则保留类和类方法,否则将被混淆。
# keepclasseswithmembernames:如果当前类中包含指定的方法,则保留类和类方法,如果类方法没有被引用,则会被移除。
-keep 关键字
一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆
-keep class com.thc.test.*两颗星表示把本包和所含子包下的类名都保持;
-keep class com.thc.test.**既可以保持该包下的类名,又可以保持类里面的内容不被混淆;
-keep class com.thc.test.*{*;}既可以保持该包及子包下的类名,又可以保持类里面的内容不被混淆;
-keep class com.thc.test.**{*;}
-keepclassmembers 不混淆该类下的特定方法
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}#保持该类下的所有public 方法不被混淆
-keepclassmembers class com.handscape.fun.SanitationJs{
public *;
}
#--保持IndexEntity下的内部类MySetting里面的所有方法不混淆
-keepclassmembers class com.handscape.entity.IndexEntity$MySetting{
*;
}-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}-keepclassmembers class **.R$* {
public static <fields>;
}
keepclassmembernames:只保留类中的方法,但如果方法没有被引用将被删除。
-keepclasseswithmembers class * {
@android.support.annotation.Keep <methods>;
}
其他关键字的使用
-dontwarn
-skipnonpubliclibraryclasses #指定读取引用库文件的时候跳过非public类。这样做可以提高处理速度并节省内存。一般情况下非 public在应用内是引用不到的,跳过它们也没什么关系。但是,在一些java类库中中出现了public类 继承非public类的情况,这样就不能用这个选项了。这种情况下,会打印一个警告出来,提示找不到类。
# 指定不去忽略非公共库的类成员 -dontskipnonpubliclibraryclassmembers
# 抛出异常时保留代码行号 -keepattributes SourceFile,LineNumberTable
6、错误说明
1、Proguard打包混淆报错:can't find superclass or interface
找到报错误的类,发现如果是当前版本Android sdk没有,说明该第三方包在更高版的sdk下编译的,将所在项目的 compileSdkVersion 版本调高就行了。
2、build Variants 里面切换为release ,然后rebild clean 后,百度地图会报找不到 okhttp和google gson 包
3、导入第三方包
-libraryjars libs/android.support.v4.jar
***注意:在Android studio 中不需要导入第三方类库的代码,如上面的部分:-libraryjars libs/BaiduLBS_Android.jar等,按照网上说的,需要在混淆文件中导入第三方类库,防止混淆时读取包内容出错,但是如果那样做的话,会报如下错误:
是的,他说这个包被指定了两次
原因是,在build.gradle文件中已经指定了第三方类库,这里再次指定,就会重复,所以,在此应该不需要导第三方包的代码
-libraryjars libs/android.support.v4.jar这个。