Class not found when unmarshalling when Proguard enabled

3.3k views Asked by At

I've been dealing with what seems to be a Proguard configuration problem. My app is working fine when in debug, but the released apk gives me an exception when launching 2nd activity. It seems to happen when trying to retrieve Parcelable extras from Intent.

Here is the stacktrace :

E/Parcel﹕ Class not found when unmarshalling: com.mycompany.model.Task
    java.lang.ClassNotFoundException: com.mycompany.model.Task
            at java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:251)
            at java.lang.Class.forName(Class.java:216)
            at android.os.Parcel.readParcelableCreator(Parcel.java:2133)
            at android.os.Parcel.readParcelable(Parcel.java:2097)
            at android.os.Parcel.readValue(Parcel.java:2013)
            at android.os.Parcel.readArrayMapInternal(Parcel.java:2314)
            at android.os.Bundle.unparcel(Bundle.java:249)
            at android.os.Bundle.getString(Bundle.java:1118)
            at android.content.Intent.getStringExtra(Intent.java:5128)
            at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1447)
            at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:1043)
            at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:4054)
            at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3948)
            at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:159)
            at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2629)
            at android.os.Binder.execTransact(Binder.java:404)
            at dalvik.system.NativeStart.run(Native Method)
Caused by: java.lang.NoClassDefFoundError: com/mycompany/model/Task
            at java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:251)
            at java.lang.Class.forName(Class.java:216)
            at android.os.Parcel.readParcelableCreator(Parcel.java:2133)
            at android.os.Parcel.readParcelable(Parcel.java:2097)
            at android.os.Parcel.readValue(Parcel.java:2013)
            at android.os.Parcel.readArrayMapInternal(Parcel.java:2314)
            at android.os.Bundle.unparcel(Bundle.java:249)
            at android.os.Bundle.getString(Bundle.java:1118)
            at android.content.Intent.getStringExtra(Intent.java:5128)
            at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1447)
            at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:1043)
            at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:4054)
            at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3948)
            at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:159)
            at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2629)
            at android.os.Binder.execTransact(Binder.java:404)
            at dalvik.system.NativeStart.run(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.mycompany.model.Task" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
            at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:67)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:497)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:457)
            at java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:251)
            at java.lang.Class.forName(Class.java:216)
            at android.os.Parcel.readParcelableCreator(Parcel.java:2133)
            at android.os.Parcel.readParcelable(Parcel.java:2097)
            at android.os.Parcel.readValue(Parcel.java:2013)
            at android.os.Parcel.readArrayMapInternal(Parcel.java:2314)
            at android.os.Bundle.unparcel(Bundle.java:249)
            at android.os.Bundle.getString(Bundle.java:1118)
            at android.content.Intent.getStringExtra(Intent.java:5128)
            at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1447)
            at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:1043)
            at com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:4054)
            at com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:3948)
            at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:159)
            at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2629)
            at android.os.Binder.execTransact(Binder.java:404)
            at dalvik.system.NativeStart.run(Native Method)

There is the Parcelable Task model :

public class Task implements Parcelable {

    public enum Mode {MODE1, MODE2}

    public enum Type {TYPE1, TYPE2, TYPE3}

    private final String id;
    private final Mode mode;
    private final Type type;
    private ArrayList<Character> charactersList;

    public Task(String id, ArrayList<Character> characters, Mode mode, Type type) {
        this.id = id;
        this.charactersList = characters;
        this.mode = mode;
        this.type = type;
    }

    public Mode getMode() {
        return mode;
    }

    public Type getType() {
        return type;
    }

    public String getId() {
        return id;
    }

    public ArrayList<Character> getCharactersList() {
        return charactersList;
    }

    private Task(Parcel in) {
        id = in.readString();

        Parcelable[] parcelablecharactersArray = in.readParcelableArray(Character.class.getClassLoader());
        charactersList = new ArrayList<>();
        for (Parcelable p : parcelablecharactersArray) {
            charactersList.add((Character) p);
        }

        mode = Mode.valueOf(in.readString());
        type = Type.valueOf(in.readString());

    }

    public static final Parcelable.Creator<Task> CREATOR = new Parcelable.Creator<Task>() {

        public Task createFromParcel(Parcel in) {
            return new Task(in);
        }

        public Task[] newArray(int size) {
            return new Task[size];
        }
    };

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeParcelableArray(charactersList.toArray(new Parcelable[charactersList.size()]), 0);
        dest.writeString(mode.name());
        dest.writeString(type.name());

    }

}

And there is the activity launching and receiving the intent :

public class TaskActivity {

    private Task mTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setBackgroundDrawable(null);

        Bundle extras = getIntent().getExtras();
        mTask = extras.getParcelable(Constants.TASK_KEY);
    }

    private void launchNextTask(Task nextTask) {
        if (nextTask != null) {
            Intent intent = new Intent(TaskActivity.this, TaskActivity.class);
            intent.putExtra(Constants.TASK_KEY, nextTask);
            startActivity(intent);
        }
    }
}

And the proguard-rules :

-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }

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

-keep public class com.mycompany.model.Task { *; }
-keep public class com.mycompany.model.Character { *; }
-keep public class com.mycompany.Constants { *; }

Do you have an idea what the problem could be ? My first guess is it's a problem with classloader when trying to create Character ArrayList in Parcelable constructor (by the way, Character implements Parcelable too). Second guess is the enums.

2

There are 2 answers

2
NicolasF On BEST ANSWER

Found the answer. Proguard configuration it was. But it was not about Parcelable or Task, it was about other classes in another module that were obfuscated and should not have been.

Adding

-keep public class com.othermodule.OtherClass { *; }

did the trick.

So the stacktrace was kind of misleading, or at least not explicit enough.

Anyway, thank you @shkschneider for your help.

0
shkschneider On

I think the problem comes from Parcelable which Task extends.

# could be refined, but if it works you got the idea
-keepnames class * implements android.os.Parcelable { *; }
-keepclassmembers class * implements android.os.Parcelable { *; }
-keep public class com.mycompany.model.Task { *; }