Il semble que j'obtienne une étrange erreur dans mon application (voir GitHub ), qui se produit lorsque je passe des objets à différentes activités qui implémentent Parcelable
.
J'ai vérifié d'autres questions et réponses ici sur Stack Overflow, mais je n'ai pas pu trouver de solution. J'ai essayé la réponse ici , par exemple - ici c'est pour référence:
-keepclassmembers class * implements Android.os.Parcelable {
static ** CREATOR;
}
J'ai également vérifié que les appels de méthode dans writeToParcel
sont en ordre. La plupart des autres questions sur Stack Overflow concernant ce problème n'ont pas de réponses.
De plus, la raison pour laquelle je pose une nouvelle question est parce que je pense que mon problème est dû à la façon dont j'ai utilisé les interfaces dans mon application (je développerai ce point plus tard). D'autres questions sur Stack Overflow ne conviendraient pas à mon scénario particulier.
Dans ce qui suit, j'ai fourni des liens vers le code via GitHub, afin que vous puissiez explorer davantage le code si nécessaire.
Lorsque je clique sur un bouton pour lancer une nouvelle activité (passer un objet qui implémente Parcelable
), il y a un crash :
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)
...
J'appelle ainsi l'activité susmentionnée (aussi voir 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);
La partie principale à considérer est quand je passe mTopic
. C'est un Topic
interface que j'ai créé.
Cependant, l'interface Topic
étend Parcelable
et donc les objets qui implémentent Topic
incluent également le constructeur, le champ CREATOR
et les méthodes qu'une classe implémentant Parcelable
devrait normalement avoir.
Vous pouvez afficher les classes pertinentes via les liens GitHub, mais je fournirai les parties pertinentes de ces classes ci-dessous. Voici l'interface Topic
:
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;
}
...
}
}
Une classe (objet) qui implements 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);
}
}
Dans l'une des dernières lignes du code ci-dessus, vous pouvez voir que je passe mCourse
, qui est un objet Course
que j'ai créé. C'est ici:
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);
}
}
Je soupçonne que quelque chose ici peut être à l'origine du problème, et c'est la raison pour laquelle mon scénario est différent de ceux des autres questions.
Pour être honnête, je ne sais pas exactement ce qui peut causer l'erreur, donc des explications et des conseils dans les réponses seraient très appréciés.
Modifier :
Après les suggestions de David Wasser, j'ai mis à jour des parties de mon code comme ceci:
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();
J'ai ajouté des messages de journal en utilisant Log.d(...)
pour voir si des variables sont nulles lors de leur passage dans writeToParcel(...)
et j'ai utilisé la méthode de David Wasser pour correctement gérer cela.
Cependant, je reçois toujours le même message d'erreur.
Votre problème se trouve dans LanguagesFlashCard
. Voici vos méthodes de colis/non-colis:
protected LanguagesFlashCard(Parcel in) {
mId = in.readInt();
mEnglish = in.readString();
mAnswerPrefix = in.readString();
mAnswer = in.readString();
mTier = in.readInt();
mTopic = in.readParcelable(Topic.class.getClassLoader());
}
Comme vous pouvez le voir, ils ne correspondent pas. Le deuxième élément que vous écrivez dans le Parcel
est un int
, le deuxième élément que vous lisez dans le Parcel
est un String
.
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeInt(mTier);
dest.writeString(mEnglish);
dest.writeString(mAnswerPrefix);
dest.writeString(mAnswer);
dest.writeParcelable(mTopic, flags);
}