2016-06-23 12 views
6

Wygląda na to, że dostaję dziwny błąd w mojej aplikacji (zobacz GitHub), który występuje, gdy przekazuję obiekty do różnych działań, które implementują Parcelable.Wyjątek java.lang.RuntimeException: Paczka android.os.Parcel: Unmarshalling nieznany kod typu

Sprawdziłem inne pytania i odpowiedzi tutaj w Stack Overflow, ale nie mogłem znaleźć rozwiązania. Próbowałem odpowiedź here, na przykład - to jest tutaj dla odniesienia:

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

Ja również upewnić się, że metoda nazywa się writeToParcel są w porządku. Większość innych pytań dotyczących przepełnienia stosu dotyczących tego problemu nie ma odpowiedzi.

Co więcej, powodem, dla którego zadaję nowe pytanie, jest to, że myślę, że mój problem wynika z tego, w jaki sposób korzystałem z interfejsów w mojej aplikacji (później omówię tę kwestię). Inne pytania dotyczące Stack Overflow nie pasują do mojego konkretnego scenariusza.

Poniżej podam linki do kodu przez GitHub, aby w razie potrzeby móc odkryć więcej kodu.


Po kliknięciu na przycisk do launch a new activity (przekazując obiekt, który implementuje Parcelable), there is a 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 [email protected]: Unmarshalling unknown type code 6815860 at offset 200 
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416) 
    ... 
Caused by: java.lang.RuntimeException: Parcel [email protected]: 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) 
... 

nazywam wspomnianą aktywność jak SO (również see 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); 

Główną rolę do wzięcia pod uwagę to, kiedy przekażę mTopic. To jest Topicinterface, które utworzyłem.

Jednak interfejs Topic rozciąga Parcelable a więc przedmioty, które implementują Topic obejmują również konstruktor CREATOR Field i metod, że klasa realizacji Parcelable normalnie mieć.

Możesz przeglądać odpowiednie klasy przez linki GitHub, ale podam odpowiednie części tych klas poniżej. Oto interfejs 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; 
     } 

     ... 
    } 

} 

klasy A (object), które 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); 
    } 

} 

W jednym z ostatnich wierszy kodu powyżej, widać mijam mCourse, który jest Course obiekt, który stworzyłem. Oto ona:

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); 
    } 

}

podejrzewam coś tu może być przyczyną problemu, i jest powodem mój scenariusz różni się od tych w innych pytań.


Szczerze mówiąc, nie jestem do końca pewien, co może być przyczyną błędu, więc wyjaśnienia i wytyczne zawarte w odpowiedzi będą mile widziane.


Edit:

Po sugestie David Wasser „s, I zostały zaktualizowane części mojego kodu tak:

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(); 

Dodałem wiadomości dziennika używając Log.d(...) aby sprawdzić, czy wszelkie zmienne są nieważne kiedy były przekazywane w writeToParcel(...) i stosowane metody David Wasser jest prawidłowo załatw to.

Mimo to nadal otrzymuję ten sam komunikat o błędzie.

+0

Napisz kod dotyczący paczkowania i nieparowania dla rzeczywistych klas, które są wpisane w 'Intent'. –

+0

@DavidWasser Zaktualizowałem swoją odpowiedź, aby pokazać kod dla klas "Intent" (można je wyświetlić w całości za pośrednictwem podanych linków GitHub). –

Odpowiedz

17

Twój problem jest w LanguagesFlashCard. Oto metody paczki/nietypowe:

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

Jak widać, nie pasują. Drugą pozycją, którą napiszesz na Parcel jest int, drugą rzeczą, którą czytasz z Parcel jest 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); 
} 
+1

Dziękuję - to rozwiązało mój problem! –

+0

Czy kolejność wartości może ulec zmianie, jeśli wszystkie są tego samego typu. Mam na myśli mEnglish = in.readString(); mAnswerPrefix = in.readString(); mAnswer = in.readString(); można odczytać jako dest.writeString (mAnswer); dest.writeString (mPolski); dest.writeString (mAnswerPrefix); –

+1

@UsmanRana Nie. Musisz odczytać i zapisać wartości w dokładnie tej samej kolejności, bez względu na to, jakie są. Serializujesz i deserializujesz do/od tablicy bajtów. 'Parcel' nie jest' HashMap'. –

Powiązane problemy