android - Proguard 和 Object extends Parcelable - 解码时找不到类

标签 android proguard parcelable

我正在发布我的 apk。在 Debug模式下,我没有错误。 当我使用 Proguard 签署我的 apk 时,我的应用程序一直运行到第四个屏幕,在那里我必须处理一个实现 Parcelable 的值对象类

 E/Parcel(811): Class not found when unmarshalling: com.spg.movil.vo.Pdv
 E/Parcel(811): java.lang.ClassNotFoundException: com.spg.movil.vo.Pdv
 E/Parcel(811): at java.lang.Class.classForName(Native Method)
 E/Parcel(811): at java.lang.Class.forName(Class.java:251)
 E/Parcel(811): at java.lang.Class.forName(Class.java:216)
 E/Parcel(811): at android.os.Parcel.readParcelableCreator(Parcel.java:2133)
 E/Parcel(811): at android.os.Parcel.readParcelable(Parcel.java:2097)
 E/Parcel(811): at android.os.Parcel.readValue(Parcel.java:2013)
 E/Parcel(811): at android.os.Parcel.readArrayMapInternal(Parcel.java:2314)
 E/Parcel(811): at android.os.Bundle.unparcel(Bundle.java:249)
 E/Parcel(811): at android.os.Bundle.getString(Bundle.java:1118)
 E/Parcel(811): at android.content.Intent.getStringExtra(Intent.java:4991)
 E/Parcel(811): at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1353)
 E/Parcel(811): at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:977)
 E/Parcel(811): at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:3936)
 E/Parcel(811): at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3839)
 E/Parcel(811): at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:159)
 E/Parcel(811): at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2546)
 E/Parcel(811): at android.os.Binder.execTransact(Binder.java:404)
 E/Parcel(811): at dalvik.system.NativeStart.run(Native Method)
 E/Parcel(811): Caused by: java.lang.NoClassDefFoundError: com/spg/movil/vo/Pdv
 E/Parcel(811): ... 18 more
 E/Parcel(811): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.spg.movil.vo.Pdv" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
 E/Parcel(811): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:67)
 E/Parcel(811): at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
 E/Parcel(811): at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
 E/Parcel(811): ... 18 more

这是我的 VO 类:

public class Pdv implements Parcelable {

int id;
int clientId;
String canal;
String formato;
String ciudad;
String region;
int ndeterminante;
String sucursal;
int nielsen;
String tienda;
String calle;
String colonia;
int CP;
int activo;
String cadena;
int estadoId;
String estado;

double latitude;
double longitude;
String telefono;
String contacto;
String horarioIni;
String horarioFin;

Proyecto proyecto;
int idRuta;
int done;
int sincronizado;

// Not include in parcelable
Marker marker;

public Marker getMarker() {
    return marker;
}

public void setMarker(Marker marker) {
    this.marker = marker;
}



@Override
public int describeContents() {
    return 0;
}


public Pdv(int id, int clientId, String canal, String formato, String ciudad, String region, int ndeterminante,
        String sucursal, int nielsen, String tienda, String calle, String colonia, int cP, int activo,
        double latitude, double longitude, String telefono, String contacto, String horarioIni, String horarioFin,
        Proyecto proyecto, int done, int sincronizado, String cadena, String estado) {
    super();
    this.id = id;
    this.clientId = clientId;
    this.canal = canal;
    this.formato = formato;
    this.ciudad = ciudad;
    this.region = region;
    this.ndeterminante = ndeterminante;
    this.sucursal = sucursal;
    this.nielsen = nielsen;
    this.tienda = tienda;
    this.calle = calle;
    this.colonia = colonia;
    this.CP = cP;
    this.cadena = cadena;
    this.estado = estado;
    this.activo = activo;
    this.latitude = latitude;
    this.longitude = longitude;
    this.telefono = telefono;
    this.contacto = contacto;
    this.horarioIni = horarioIni;
    this.horarioFin = horarioFin;
    this.proyecto = proyecto;
    this.done = done;
    this.sincronizado = sincronizado;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(id);
    dest.writeInt(clientId);
    dest.writeString(canal);
    dest.writeString(formato);
    dest.writeString(ciudad);
    dest.writeString(region);
    dest.writeInt(ndeterminante);
    dest.writeString(sucursal);
    dest.writeInt(nielsen);
    dest.writeString(tienda);
    dest.writeString(calle);
    dest.writeString(colonia);
    dest.writeString(cadena);
    dest.writeString(estado);
    dest.writeInt(estadoId);
    dest.writeInt(CP);
    dest.writeInt(activo);
    dest.writeInt(done);
    dest.writeInt(sincronizado);
    dest.writeInt(idRuta);
    dest.writeDouble(latitude);
    dest.writeDouble(longitude);
    dest.writeString(telefono);
    dest.writeString(contacto);
    dest.writeString(horarioIni);
    dest.writeString(horarioFin);
    dest.writeParcelable(proyecto, 0);

}

public static final Parcelable.Creator<Pdv> CREATOR = new Parcelable.Creator<Pdv>() {
    @Override
    public Pdv createFromParcel(Parcel source) {
        return new Pdv(source);
    }

    @Override
    public Pdv[] newArray(int size) {
        return new Pdv[size];
    }
};

public Pdv(Parcel in) {


    this.id = in.readInt();
    this.clientId = in.readInt();
    this.canal = in.readString();
    this.formato = in.readString();
    this.ciudad = in.readString();
    this.region = in.readString();
    this.ndeterminante = in.readInt();
    this.sucursal = in.readString();
    this.nielsen = in.readInt();
    this.tienda = in.readString();
    this.calle = in.readString();
    this.colonia = in.readString();
    this.cadena = in.readString();
    this.estado = in.readString();
    this.estadoId = in.readInt();
    this.CP = in.readInt();
    this.activo = in.readInt();
    this.done = in.readInt();
    this.sincronizado = in.readInt();
    this.idRuta = in.readInt();

    this.latitude = in.readDouble();
    this.longitude = in.readDouble();

    this.telefono = in.readString();
    this.contacto = in.readString();
    this.horarioIni = in.readString();
    this.horarioFin = in.readString();
    this.proyecto = in.readParcelable(Proyecto.class.getClassLoader());

    }
// getters and setters ...
}

这是我的 proguard.project.txt

    ##---------------Begin: proguard configuration common for all Android apps ----------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-dump class_files.txt 
-printseeds seeds.txt 
-printusage unused.txt 
-printmapping mapping.txt 
-optimizations !code/simplification/arithmetic


-injars      bin/classes
-injars      libs
-outjars     bin/classes-processed.jar



-allowaccessmodification
-keepattributes *Annotation*
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
-repackageclasses ''


-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

-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*(...);
}

# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
-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();
}


# Preserve all native method names and the names of their classes.
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}
-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>;
}

-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}


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


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


# Preserve static fields of inner classes of R classes that might be accessed
# through introspection.
-keepclassmembers class **.R$* {
  public static <fields>;
}


# Preserve the special static methods that are required in all enumeration classes.
-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 *;
}
##---------------End: proguard configuration common for all Android apps ----------


##---------------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


# 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.** { *; }


##---------------End: proguard configuration for Gson  ----------

##---------------Begin proguard configuration for Google Maps --------------------

-keep class com.google.android.gms.** { *; }
-dontwarn com.google.android.gms.**

-keep class org.** { *; }
-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 * {
    @com.google.android.gms.common.annotation.KeepName *;
}


-renamesourcefileattribute SourceFile    
-keepattributes SourceFile,LineNumberTable

##---------------End proguard configuration for Google Maps --------------------

##---------------Begin proguard configuration for my libs  -----------------------
-keep class com.todddavies.components.progressbar.** { *; }
-dontwarn com.todddavies.components.progressbar.**

-dontwarn org.apache.**


-keepclassmembers class * implements android.os.Parcelable {
    static ** CREATOR;
}

我已经看过好几个帖子了,但是没有一个能解决我的问题

Proguard causing RuntimeException (Unmarshalling unknown type code) in Parcelable class

我可以将 parcelable 更改为 json 字符串,但我还有其他 parcelables 对象,我担心将来会遇到其他类似问题。

任何帮助将不胜感激!

最佳答案

当 Android 尝试从通过 IPC 传递的 Bundle 中读取任何值(通过 startActivity、sendBroadcast 等发送)时,它会解包整个 Bundle 中的所有内容。如您所见,如果 Android 遇到任何不在您的类路径中的类,它就会抛出 java.lang.ClassNotFoundException

这里有一些尝试,您可能需要其中的一个或多个:

在您的 proguard.pro 文件中包含以下内容,以保留 Parcelables 的 CREATOR,Android 通过反射引用它(我认为 Google 会自动将此规则添加到每个应用程序,但有时它似乎没有取决于您的构建系统):​​

# keep CREATOR for referenced parcelables since it is accessed via reflection
-keepclassmembers class * implements android.os.Parcelable {
    static ** CREATOR;
}

在从 Bundle 中获取任何东西之前添加类加载器。这假设此代码是从您的 APK 中的类运行的(因此与 extra 中的自定义 Parcelable 类共享相同的类加载器)或者您可以传递 Context 并使用 Context #getClassLoader() 如果您在静态方法中:

extras.setClassLoader(getClass().getClassLoader());

如果您的 Bundle 中有您实际上没有使用但在您的类路径中的 Parceables,那么您可以使用此规则保留所有 Parceables:

# Keep all known parcelables
-keep public class * implements android.os.Parcelable

最后,如果您收到一个带有 ParcelableBundle,而应用程序在构建时不知道该类,则 Bundle 是很可能中毒,你根本无法使用它。这是让您不喜欢的其他 Android 应用程序崩溃的好方法!您能做的最好的事情就是 try catch 您第一次访问 Bundle 的代码块,以避免应用程序崩溃。

try {
  Bundle extras = getIntent().getExtras();
  if (extras != null) {
    // Trigger unparcel
    extras.size();
  }
} catch (BadParcelableException e) {
  Log.w(TAG, "Got poison Bundle or programmer needs to fix proguard/classloader", e);
}

关于android - Proguard 和 Object extends Parcelable - 解码时找不到类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25770049/

相关文章:

android - 发布带广告和不带广告的安卓应用

android - Base64 encodeBytes - 内存不足异常

android - 将位图转换为字符串

obfuscation - 如何避免混淆器混淆用@OnStart 注释的类

Android 应用程序在使用 proguard 发布时崩溃

android - 从 Android 中的 Parcelable 数组中获取值(value)

java - Android - 如何检测 compass 度数的变化(Java)

android - proguard - AppCompat 不支持 proguard/minify 后的当前主题功能

android - 在android中的activity之间传递对象为什么要Parcelable?为什么不是 JSON 字符串?

java - 解码错误时未找到类。可使用 byteArray、byte[] 进行解析