使用启用了 proguard 的 GSON

Posted

技术标签:

【中文标题】使用启用了 proguard 的 GSON【英文标题】:Using GSON with proguard enabled 【发布时间】:2015-10-28 22:16:43 【问题描述】:

我的代码在没有 proguard 的情况下可以完美运行,但是在启用 proguard 时 GSON 不起作用。

这是代码中不起作用的部分

JSONArray mensaje = response.getJSONArray("categorias");
// Parsear con Gson
Categorias[] categorias = gson.fromJson(mensaje.toString(), Categorias[].class);
Log.d("mainfragment","desc categoria "+categorias[0].getDescripcionCategoria());

mainfragment 的日志打印为 null

# Output
D/Dato categorias﹕ Respuesta: "categorias":["idCategoria":"22","imagenCategoria":"ic_aseguradora","descripcionCategoria":"Aseguradoras","idCategoria":"24","imagenCategoria":"ic_bar","descripcionCategoria":"Bares","idCategoria":"12","imagenCategoria":"ic_boutique","descripcionCategoria":"Boutiques","idCategoria":"6","imagenCategoria":"ic_cafe","descripcionCategoria":"Cafeterias","idCategoria":"21","imagenCategoria":"ic_ciber","descripcionCategoria":"Ciber","idCategoria":"10","imagenCategoria":"ic_estetica","descripcionCategoria":"Estéticas","idCategoria":"1","imagenCategoria":"ic_farmacia","descripcionCategoria":"Farmacias","idCategoria":"7","imagenCategoria":"ic_ferreteria","descripcionCategoria":"Ferreterias","idCategoria":"16","imagenCategoria":"ic_gas","descripcionCategoria":"Gas","idCategoria":"23","imagenCategoria":"ic_gasolinera","descripcionCategoria":"Gasolineras","idCategoria":"4","imagenCategoria":"ic_gym","descripcionCategoria":"Gimnasios","idCategoria":"2","imagenCategoria":"ic_hotel","descripcionCategoria":"Hoteles","idCategoria":"15","imagenCategoria":"ic_lavanderia","descripcionCategoria":"Lavanderias","idCategoria":"19","imagenCategoria":"ic_muebleria","descripcionCategoria":"Mueblerias","idCategoria":"25","imagenCategoria":"ic_optica","descripcionCategoria":"Ópticas","idCategoria":"20","imagenCategoria":"ic_pasteleria","descripcionCategoria":"Pastelerias","idCategoria":"17","imagenCategoria":"ic_pizza","descripcionCategoria":"Pizzerías","idCategoria":"13","imagenCategoria":"ic_purificadora","descripcionCategoria":"Purificadoras de agua","idCategoria":"5","imagenCategoria":"ic_restaurant","descripcionCategoria":"Restaurantes","idCategoria":"11","imagenCategoria":"ic_ropa","descripcionCategoria":"Ropa","idCategoria":"9","imagenCategoria":"ic_salon","descripcionCategoria":"Salones de fiestas","idCategoria":"3","imagenCategoria":"ic_sonido","descripcionCategoria":"Sonidos","idCategoria":"28","imagenCategoria":"ic_taqueria","descripcionCategoria":"Taquería","idCategoria":"8","imagenCategoria":"ic_taxi","descripcionCategoria":"Taxis","idCategoria":"14","imagenCategoria":"ic_tortilleria","descripcionCategoria":"Tortillerias","idCategoria":"27","imagenCategoria":"ic_veterinaria","descripcionCategoria":"Veterinarias","idCategoria":"18","imagenCategoria":"ic_vinateria","descripcionCategoria":"Vinaterías","idCategoria":"26","imagenCategoria":"ic_zapateria","descripcionCategoria":"Zapaterías"],"estado":1
D/mainfragment﹕ desc categoria null

这是我的proguard规则

#GoogleMaps
-keep class * extends java.util.ListResourceBundle 
    protected Object[][] getContents();


-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable 
    public static final *** NULL;


-keepnames @com.google.android.gms.common.annotation.KeepName class *
-keepclassmembernames class * 
    @ccom.google.android.gms.common.annotation.KeepName *;


-keepnames class * implements android.os.Parcelable 
    public static final ** CREATOR;




#Volley?

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*


-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
-dontnote com.android.vending.licensing.ILicensingService

-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();
 


 -keepclasseswithmembernames class * 
     native <methods>;
 


 -keepclasseswithmembers class * 
     public <init>(android.content.Context, android.util.AttributeSet);
 


 -keepclasseswithmembers class * 
     public <init>(android.content.Context, android.util.AttributeSet, int);
 


-keepclassmembers class **.R$* 
   public static <fields>;
 

 -keepclassmembers class * extends android.app.Activity 
    public void *(android.view.View);
 


 -keepclassmembers enum * 
     public static **[] values();
     public static ** valueOf(java.lang.String);
 

-keep public class * 
     public protected *;
 



 -keep class * implements android.os.Parcelable 
   public static final android.os.Parcelable$Creator *;
 

 -keep class android.support.v7.widget.SearchView
 -keep class android.support.v7.widget.***;
 -keep class android.support.v4.app.**  *; 
 -keep interface android.support.v4.app.**  *; 
 -keep class com.actionbarsherlock.**  *; 
 -keep interface com.actionbarsherlock.**  *; 
 -keep class com.android.volley.**  *; 
 -keep interface com.android.volley.**  *; 
 -keepattributes *Annotation*


##---------------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*
-keepattributes EnclosingMethod

# 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 Categorias.data.model.**  *; 
 -keep class Categorias.**  *; 

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.**  *; 
-keep class com.puertosoft.appcomercialdelpuerto.android.model.**  *; 
-keep class com.puertosoft.appcomercialdelpuerto.model.**  *; 
-keep class com.puertosoft.appcomercialdelpuerto.model.User  *; 

# Add the gson class
-keep public class com.google.gson

# Add any classes the interact with gson
-keep class com.puertosoft.appcomercialdelpuerto.models.ChatModel  *; 
-keep class com.puertosoft.appcomercialdelpuerto.models.FeedModel  *; 
-keep class com.puertosoft.appcomercialdelpuerto.android.models.ChatModel  *; 
-keep class com.puertosoft.appcomercialdelpuerto.android.models.FeedModel  *; 

-libraryjars /build/intermediates/pre-dexed/debug/gson-2.3-08958b96da94c86264ec30e35a9d524bac95d2df.jar




-printmapping outputfile.txt
-renamesourcefileattribute SourceFile
#-keepattributes SourceFile,LineNumberTable

这是受影响的类

public class Categorias 

    private String idCategoria;
    private String descripcionCategoria;
    private String imagenCategoria;

    public Categorias(String idCategoria, String descripcionCategoria, String imagenCategoria)
        this.idCategoria=idCategoria;
        this.descripcionCategoria=descripcionCategoria;
        this.imagenCategoria=imagenCategoria;
    

    public String getIdCategoria()
        return idCategoria;
    

    public String getDescripcionCategoria()
        return descripcionCategoria;
    

    public String getImagenCategoria()
        return imagenCategoria;
    


【问题讨论】:

【参考方案1】:

变量名会被 proguard 混淆,留下类似的东西

private String a;

而不是

private String descripcionCategoria;

您可以添加 proguard 规则,这样某些类就不会被混淆。我用这些侥幸逃脱了:

-keepattributes Signature
# POJOs used with GSON
# The variable names are JSON key values and should not be obfuscated
-keepclassmembers class com.example.apps.android.Categorias  <fields>; 
# You can apply the rule to all the affected classes also
# -keepclassmembers class com.example.apps.android.model.**  <fields>; 

如果你的 POJO 类名也用于解析,那么你也应该添加规则

-keep class com.example.apps.android.model.**  <fields>; 

在你的情况下,没有使用注释,如果你这样做,你会需要这个

# Keep the annotations
-keepattributes *Annotation*

另一种解决此问题的方法是使用SerializedName 注释并让类被混淆。为此,您仍然需要 -keepattributes *Annotation* 规则。

import com.google.gson.annotations.SerializedName

@SerializedName("descripcionCategoria")
private String descripcionCategoria;

【讨论】:

类似question 你知道 POJO 类名不应该被混淆的任何用例吗? (反射除外) 非常感谢,-keep class com.company.model.** ; -keepclassmembers class com.company.model. ; 为我工作!【参考方案2】:

如果您希望您的模型仍然被混淆,请使用注释 @SerializedName("name_of_json_field")。它会让 gson 知道该字段的真实名称。

相信你也会需要

-keepattributes *Annotation*

防止您的注释被混淆

【讨论】:

【参考方案3】:

您需要排除模型类的混淆,如下所示,我排除了 in.intellicode.webservices.models 包中的所有模型类

-keep class in.intellicode.webservices.models.**  *; 
-keep class in.intellicode.models.**  *; 
-keep class in.intellicode.events.* *; 

-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe  *; 

【讨论】:

【参考方案4】:

当您将 proguard 脚本应用到模型类时,它会混淆它们的名称和属性名称。所以在混淆String descripcionCategoria; 之后你会得到类似String aaaa;的东西

Gson 通过 java 反射工作,它会在解析数据时尝试使用属性名称。因此,在对模型类应用混淆后,您将无法解析数据。

因此,从您的 proguard 脚本中排除模型类,您将能够再次解析。

【讨论】:

【参考方案5】:

使用proguard-rules 默认规则Gson

# 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
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.**  *; 

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.**  <fields>; 

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * 
  @com.google.gson.annotations.SerializedName <fields>;


# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken

来源:https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg

【讨论】:

【参考方案6】:

改变这个

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

这对我有用

 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

在您应用的 build.gradle 文件中。

仅供参考, https://developer.android.com/studio/build/shrink-code

【讨论】:

以上是关于使用启用了 proguard 的 GSON的主要内容,如果未能解决你的问题,请参考以下文章

启用 Proguard 后无法实例化片段

启用 Proguard 后“应用程序停止工作”

启用 ProGuard 规则时 Gson 解析不起作用

启用 ProGuard 会导致签名 APK 生成失败?

启用 PROGUARD - Xamarin.Android

启用 proguard,我的构建版本应用程序不会再次安装