碰到个头疼的问题,一个朋友项目混淆后打包编译出错,debug模式下正常。
断点模式下一步步检查发现:json解析以后数据放到javabean中去的,放进去的字符串是对的,解析出来的,但是javabean却是空,节操碎一地,多方查找原因后知道他项目json使用了google的gson三方包,于是猜想混淆的时候出了问题。
然后陪同一起查看混淆文本,一番下来基本的混淆文件都没问题,然后查找第三方包,
他说明明已经按照gson的官方文档,把混淆脚本加上去了,却还是出问题。
我后面又去查了次官方文档:google官方的proguard的文档如下,请大家一定要注意倒数第二行,class 后方到**签名的 这一段包名应该是你所有的java bean定义的目录,其实官方注释也写明了“Application classes that will be serialized/deserialized over Gson”(将通过Gson序列化/反序列化的应用程序类),通常我们cope后直接就用了没有去注意这个细节(lll¬ω¬),然后打包时Gson不能正常使用。ps:建议大家在写代码时把java bean 放在一个独立的包中,以便这行混淆文本简洁。
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; } ##这里包名需要改成自己的java bean包
##---------------End: proguard configuration for Gson ----------
以上就是Gson混淆解决方法,记录下以免自己也犯同样错误。
当然写篇文章不可能就这么短,那么接下来整理下混淆相关知识
混淆过程会有如下几个功能:
- 压缩。移除无效的类、类成员、方法、属性等;
- 优化。分析和优化方法的二进制代码;根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
- 混淆。把类名、属性名、方法名替换为简短且无意义的名称;
- 预校验。添加预校验信息。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。
这四个流程默认开启。在 Android 项目中我们可以选择将“优化”和“预校验”关闭,对应命令是-dontoptimize、-dontpreverify(当然,默认的 proguard-android.txt 文件已包含这两条混淆命令,不需要开发者额外配置)。
科普下混淆基本指令区指令的含义,也是查资料得到的了(^_^)
- 代码混淆的压缩比例,值在0-7之间
-optimizationpasses 5 - 混淆后类名都为小写
-dontusemixedcaseclassnames - 指定不去忽略非公共的库的类
-dontskipnonpubliclibraryclasses - 指定不去忽略非公共的库的类的成员
-dontskipnonpubliclibraryclassmembers - 不做预校验的操作
-dontpreverify - 生成原类名和混淆后的类名的映射文件
-verbose
-printmapping proguardMapping.txt - 指定混淆是采用的算法
-optimizations !code/simplification/cast,!field/,!class/merging/ - 不混淆Annotation
-keepattributes Annotation,InnerClasses - 不混淆泛型
-keepattributes Signature - 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
以上可以了解,有兴趣的可以去调试下((lll¬ω¬)—>自己很多都没用过)
接下来介绍下比较常用的:
- -keep 防止类和成员被移除或者被重命名
- -keep class XXXX 保留类名不变,也就是类名不混淆,而类中的成员名不保证
- -keepnames 防止类和成员被重命名
- -keepclassmembers 防止成员被移除或者被重命名
- -keepnames 防止成员被重命名
- -keepclasseswithmembers 防止拥有该成员的类和成员被移除或者被重命名
- -keepclasseswithmembers class XXXX保留类名和成员名。当然也可以是类中特定方法
- -keepclasseswithmembernames 防止拥有该成员的类和成员被重命名
想要继续了解的可以参考以下几篇文章:
Android安全攻防战,反编译与混淆技术完全解析(下)
Android混淆从入门到精通
Android代码混淆之ProGuard
最后附上稍作整理的 proguard-rules.pro 文档,各位可以在此基础上更改或添加以用于自己项目
#指定代码的压缩级别
-optimizationpasses 5
#包明不混合大小写
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
#优化 不优化输入的类文件
-dontoptimize
#预校验
-dontpreverify
#混淆时是否记录日志
-verbose
#保护注解
-keepattributes *Annotation*
# 保持哪些类不被混淆
-keep public class * extends android.app.Fragment
-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.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
#如果有引用v4包可以添加下面这行
-keep public class * extends android.support.v4.app.Fragment
#忽略警告
-ignorewarning
##记录生成的日志数据,gradle build时在本项目根目录输出##
#apk 包内所有 class 的内部结构
-dump proguard/class_files.txt
#未混淆的类和成员
-printseeds proguard/seeds.txt
#列出从 apk 中删除的代码
-printusage proguard/unused.txt
#混淆前后的映射
-printmapping proguard/mapping.txt
########记录生成的日志数据,gradle build时 在本项目根目录输出-end######
#如果引用了v4或者v7包
-dontwarn android.support.**
####混淆保护自己项目的部分代码以及引用的第三方jar包library-end####
#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
#保持自定义控件类不被混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-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*(...);
}
#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable
#保持 Serializable 不被混淆并且enum 类也不被混淆
-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();
}
#保持枚举 enum 类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * {
public void *ButtonClicked(android.view.View);
}
#不混淆资源类,保持R文件不被混淆,否则,你的反射是获取不到资源id的
-keepclassmembers class **.R$* {
public static <fields>;
}
#避免混淆泛型(不写可能会出现类型转换错误,一般情况把这个加上就是了,如果混淆报错建议关掉)
#-keepattributes Signature
#移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用,另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制,也可以自己写日志文件
#-assumenosideeffects class android.util.Log {
# public static *** v(...);
# public static *** i(...);
# public static *** d(...);
# public static *** w(...);
# public static *** e(...);
#}
############################### 以上通用也可根据需求修改 ################################
#####################################################################################
#####################################################################################
######################### 第三方包混淆选项,根据需求添加 ################################
#一般查看官方文档都有
##################################### 第三方end #####################################
#####################################################################################
############# js互调的类,工程中没有直接跳过。#############
#一般你可以这样写
-keep class 你的类所在的包.** { *; }
#如果是内部类的话,你可以这样
-keepclasseswithmembers class 你的类所在的包.父类$子类 { <methods>; }
############# js互调的类end #############
# 如果使用了Gson之类的工具要使被它解析的JavaBean类即实体类不被混淆。
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; } ##这里包名需要改成自己的java bean包
##---------------End: proguard configuration for Gson ----------
#如果在当前的application module或者依赖的library module中使用了第三方的库,并不需要显式添加规则
#-libraryjars xxx
#添加了反而有可能在打包的时候遭遇同一个jar多次被指定的错误,一般只需要添加忽略警告和保持某些class不被混淆的声明。
#以libaray的形式引用了开源项目,如果不想混淆 keep 掉,在引入的module的build.gradle中设置
如有错误请指正,十分感谢!
为了向别人、向世界证明自己而努力拼搏,而一旦你真的取得了成绩,才会明白:人无须向别人证明什么,只要你能超越自己。