google的gson三方包混淆问题,附上通用proguard-rules.pro

时间:2022-09-26 19:07:59

碰到个头疼的问题,一个朋友项目混淆后打包编译出错,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混淆解决方法,记录下以免自己也犯同样错误。


当然写篇文章不可能就这么短,那么接下来整理下混淆相关知识

混淆过程会有如下几个功能:

  1. 压缩。移除无效的类、类成员、方法、属性等;
  2. 优化。分析和优化方法的二进制代码;根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
  3. 混淆。把类名、属性名、方法名替换为简短且无意义的名称;
  4. 预校验。添加预校验信息。这个预校验是作用在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.**

####混淆保护自己项目的部分代码以及引用的第三方jarlibrary-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中设置

如有错误请指正,十分感谢!


为了向别人、向世界证明自己而努力拼搏,而一旦你真的取得了成绩,才会明白:人无须向别人证明什么,只要你能超越自己。