java.lang.RuntimeException: Parcel android.os.Parcel: Desmarca o código de tipo desconhecido

Parece que estou recebendo um erro estranho no meu aplicativo (consulteGitHub), que ocorre quando passo objetos para diferentes atividades que implementamParcelable.

Verifiquei outras perguntas e respostas aqui no Stack Overflow, mas não consegui encontrar uma solução. Eu tentei a respostaaqui, por exemplo - aqui está para referência:

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

Também assegurei que o método chamawriteToParcel estão em ordem. A maioria das outras perguntas sobre Stack Overflow sobre esse problema não tem respostas.

Além disso, o motivo pelo qual estou fazendo uma nova pergunta é porque acho que meu problema foi causado por causa do modo como usei interfaces no meu aplicativo (expandirei esse ponto mais adiante). Outras perguntas sobre o estouro de pilha não se adequariam ao meu cenário específico.

A seguir, forneci links para o código via GitHub, para que você possa explorar mais o código, se necessário.

Quando clico em um botão parainiciar uma nova atividade (passando um objeto que implementaParcelable),houve um acidente:

Process: com.satsuware.flashcards, PID: 4664
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.satsuware.flashcards/com.satsumasoftware.flashcards.ui.FlashCardActivity}: java.lang.RuntimeException: Parcel android.os.Parcel@d2219e4: Unmarshalling unknown type code 6815860 at offset 200
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
    ...
 Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@d2219e4: Unmarshalling unknown type code 6815860 at offset 200
at android.os.Parcel.readValue(Parcel.java:2319)
at android.os.Parcel.readListInternal(Parcel.java:2633)
at android.os.Parcel.readArrayList(Parcel.java:1914)
at android.os.Parcel.readValue(Parcel.java:2264)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2592)
at android.os.BaseBundle.unparcel(BaseBundle.java:221)
at android.os.Bundle.getParcelable(Bundle.java:786)
at android.content.Intent.getParcelableExtra(Intent.java:5377)
at com.satsumasoftware.flashcards.ui.FlashCardActivity.onCreate(FlashCardActivity.java:71)
at android.app.Activity.performCreate(Activity.java:6237)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
...

Eu chamo a atividade mencionada dessa maneira (tambémveja GitHub):

Intent intent = new Intent(TopicDetailActivity.this, FlashCardActivity.class);
intent.putExtra(FlashCardActivity.EXTRA_TOPIC, mTopic);
intent.putExtra(FlashCardActivity.EXTRA_NUM_CARDS, mSelectedNumCards);
intent.putExtra(FlashCardActivity.EXTRA_CARD_LIST, mFilteredCards);
startActivity(intent);

A parte principal a considerar é quando passomTopic. Isto é umTopic interface que eu criei.

No entanto, oTopic interface se estendeParcelable e assim os objetos que implementamTopic também inclui o construtor,CREATOR e os métodos que uma classe implementandoParcelable normalmente teria que ter.

Você pode visualizar as classes relevantes através dos links do GitHub, mas fornecerei as partes relevantes dessas classes abaixo. Aqui está oTopic interface:

public interface Topic extends Parcelable {

    int getId();

    String getIdentifier();

    String getName();

    Course getCourse();


    ArrayList<FlashCard> getFlashCards(Context context);


    class FlashCardsRetriever {

        public static ArrayList<FlashCard> filterStandardCards(ArrayList<FlashCard> flashCards, @StandardFlashCard.ContentType int contentType) {
            ArrayList<FlashCard> filteredCards = new ArrayList<>();
            for (FlashCard flashCard : flashCards) {
                boolean isPaper2 = ((StandardFlashCard) flashCard).isPaper2();
                boolean condition;
                switch (contentType) {
                    case StandardFlashCard.PAPER_1:
                        condition = !isPaper2;
                        break;
                    case StandardFlashCard.PAPER_2:
                        condition = isPaper2;
                        break;
                    case StandardFlashCard.ALL:
                        condition = true;
                        break;
                    default:
                        throw new IllegalArgumentException("content type '" + contentType + "' is invalid");
                }
                if (condition) filteredCards.add(flashCard);
            }
            return filteredCards;
        }

        ...
    }

}

Uma classe (objeto) queimplements Topic:

public class CourseTopic implements Topic {

    ...

    public CourseTopic(int id, String identifier, String name, Course course) {
        ...
    }

    @Override
    public int getId() {
        return mId;
    }

    @Override
    public String getIdentifier() {
        return mIdentifier;
    }

    ...


    protected CourseTopic(Parcel in) {
        mId = in.readInt();
        mIdentifier = in.readString();
        mName = in.readString();
        mCourse = in.readParcelable(Course.class.getClassLoader());
    }

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

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mId);
        dest.writeString(mIdentifier);
        dest.writeString(mName);
        dest.writeParcelable(mCourse, flags);
    }

}

Em uma das últimas linhas do código acima, você pode ver que eu passomCourse, que é umCourse objeto que eu criei. Aqui está:

public class Course implements Parcelable {

    ...

    public Course(String subject, String examBoard, @FlashCard.CourseType String courseType,
              String revisionGuide) {
        ...
    }


    public String getSubjectIdentifier() {
        return mSubjectIdentifier;
    }

    public String getExamBoardIdentifier() {
        return mBoardIdentifier;
    }

    public ArrayList<Topic> getTopics(Context context) {
        ArrayList<Topic> topics = new ArrayList<>();
        String filename = mSubjectIdentifier + "_" + mBoardIdentifier + "_topics.csv";
        CsvParser parser = CsvUtils.getMyParser();
        try {
            List<String[]> allRows = parser.parseAll(context.getAssets().open(filename));
            for (String[] line : allRows) {
                int id = Integer.parseInt(line[0]);
                topics.add(new CourseTopic(id, line[1], line[2], this));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return topics;
    }

    ...


    protected Course(Parcel in) {
        mSubjectIdentifier = in.readString();
        mBoardIdentifier = in.readString();
        mCourseType = in.readString();
        mRevisionGuide = in.readString();
    }

    public static final Creator<Course> CREATOR = new Creator<Course>() {
        @Override
        public Course createFromParcel(Parcel in) {
            return new Course(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mSubjectIdentifier);
        dest.writeString(mBoardIdentifier);
        dest.writeString(mCourseType);
        dest.writeString(mRevisionGuide);
    }

}

Eu suspeito que algo aqui possa estar causando o problema e é por isso que meu cenário é diferente dos de outras perguntas.

Para ser sincero, não sei exatamente o que pode estar causando o erro, portanto, explicações e orientações nas respostas serão muito apreciadas.

Editar:

Depois deDavid Wasserdas sugestões, atualizei partes do meu código da seguinte forma:

FlashCardActivity.java -onCreate(...):

Bundle extras = getIntent().getExtras();
extras.setClassLoader(Topic.class.getClassLoader());
mTopic = extras.getParcelable(EXTRA_TOPIC);

Course.java -writeToParcel(...):

dest.writeString(mSubjectIdentifier);
dest.writeString(mBoardIdentifier);
dest.writeString(mCourseType);
dest.writeInt(mRevisionGuide == null ? 0 : 1);
if (mRevisionGuide != null) dest.writeString(mRevisionGuide);

Course.java -Course(Parcel in):

mSubjectIdentifier = in.readString();
mBoardIdentifier = in.readString();
mCourseType = in.readString();
if (in.readInt() != 0) mRevisionGuide = in.readString();

Adicionei mensagens de log usandoLog.d(...) para ver se alguma variável é nula ao ser passada emwriteToParcel(...) e usadoDavid Wassermétodo para lidar com isso corretamente.

Ainda recebo a mesma mensagem de erro, no entanto.

questionAnswers(1)

yourAnswerToTheQuestion