3

I'm releasing my apk. In debug mode, I have no error. When I sign my apk with Proguard, my app works til the fourth screen, where I have to deal with a Value Object Class that implement 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

Here's my VO Class :

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 ...
}

And here is my 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;
}

I have already seen several posts, but none of them resolved my issue

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

I could change parcelable to json strings, but I have others parcelables objects, and I'm afraid to have others similar issue in the future.

Any Help will be appreciated !

Community
  • 1
  • 1
Juliatzin
  • 18,455
  • 40
  • 166
  • 325

1 Answers1

0

When Android attempts to read any value from a Bundle passed via IPC (sent through startActivity, sendBroadcast, etc) it unparcels everything in the entire Bundle. If Android encounters any classes that aren't in your classpath then it throws java.lang.ClassNotFoundException as you can see.

Here are some things try, you may need one or more of these:

Include the following in your proguard.pro file to keep the CREATOR for Parcelables which is referenced via reflection by Android (I thought that Google would add this rule automatically to every app but sometimes it appears not depending on your build system):

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

Add the classloader before getting anything out of the Bundle. This assumes that this code is being run from a class in your APK (and thus shares the same class loader as the custom Parcelable classes within the extra) or you could pass a Context and use Context#getClassLoader() if you are in a static method:

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

If there are Parcelables in your Bundle that you do not actually use but are in your classpath then you use this rule to keep all Parcelables:

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

Finally if you are passed a Bundle with a Parcelable for a class that app has no idea about at build time then the Bundle is likely poisoned and you won't be able to use it at all. This is a great way to crash other Android apps you don't like! The best you can do is try-catch the chunk of code where you first access the Bundle to avoid crashing your app.

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);
}
satur9nine
  • 13,927
  • 5
  • 80
  • 123