2013-08-08 28 views
39

Cho một lớp tùy chỉnh org.example.app.MyClass implements Parcelable, tôi muốn viết một List<MyClass> cho một bưu kiện. Tôi đã làm marshalling với"BadParcelableException: ClassNotFoundException khi unmarshalling <myclass>" trong khi sử dụng phương thức Parcel.read có một ClassLoader làm đối số

List<MyClass> myclassList = ... 
parcel.writeList(myclassList); 

bất cứ khi nào tôi cố gắng unmarshall lớp với

List<MyClass> myclassList = new ArrayList<MyClass>(); 
parcel.readList(myclassList, null); 

có một ngoại lệ "BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass".

Có gì sai ở đây? Tại sao lớp học không tìm thấy?

+1

Tôi nhận được lỗi này trong một ngữ cảnh khác - gọi 'bundle.keySet()' trên một gói có chứa Parcelable. Khi chạy một lớp thử nghiệm riêng lẻ đối với mã được đề cập, nó đã trôi qua, nhưng chạy toàn bộ bộ kiểm thử dẫn đến 'BadParcelableException'. "Fix" là để thực hiện 'bundle.setClassloader (MyClass.class.getClassLoader())' trước 'bundle.keySet()'. –

Trả lời

84

Không hủy lưu trữ một lớp tùy chỉnh, tức là một lớp được cung cấp bởi ứng dụng của bạn chứ không phải bởi khung công tác Android, với trình tải lớp khung được sử dụng khi bạn cung cấp đối số Class2. Sử dụng các bộ nạp lớp ứng dụng:

parcel.readList(myclassList, getClass().getClassLoader()); 

Bất cứ khi nào một phương pháp Parcel.read*() cũng có một ClassLoader như là đối số (ví dụ Parcel.readList(List outVal, ClassLoader loader)) và bạn muốn đọc một lớp ứng dụng từ một Parcel, sử dụng bộ nạp lớp ứng dụng có thể được lấy ra với getClass().getClassLoader() .

Nền: Android đi kèm với hai trình nạp lớp: Trình nạp lớp hệ thống, có thể tải tất cả các lớp hệ thống nhưng các lớp được cung cấp bởi ứng dụng của bạn. Và trình nạp lớp ứng dụng, đã thiết lập trình nạp lớp hệ thống làm cha mẹ và do đó có thể tải tất cả các lớp. Nếu bạn cung cấp null trình nạp lớp, Parcel.readParcelableCreator(), will use the framework class loader, gây ra trong ClassNotFoundException.

Nhờ alexanderblom cho việc cung cấp the hint dẫn tôi đi đúng hướng

16

Tôi tin rằng một hình thức chính xác hơn những điều này sẽ là:

List<MyClass> myclassList = new ArrayList<MyClass>(); 
parcel.readList(myclassList, MyClass.class.getClassLoader()); 

Bởi vì ở đây bạn đang sử dụng một cách rõ ràng bộ nạp lớp cho loại chung của Danh sách.

+6

Về cơ bản giống như câu trả lời của tôi. Không có sự khác biệt giữa 'MyClass.class' và' getClass() ', cả hai đều trả về cùng một đối tượng lớp vì' getClass() 'được gọi trong MyClass. Ngoài ra, bạn có thể sử dụng bất kỳ lớp nào ở đây, miễn là nó là một lớp tùy chỉnh và không phải là một lớp do khung công tác cung cấp. – Flow

+6

Tôi không nói rằng bạn đã sai, chỉ cần biểu mẫu này có thể phù hợp hơn. Cũng nên xem xét rằng bạn đang thực hiện một cuộc gọi phương thức bổ sung tới 'getClass()' không cần thiết khi bạn tham chiếu lớp tĩnh. –

3

tôi đã phải đối mặt với cùng một vấn đề và thay vì parcleable Tôi đã từng bó

intent.setExtrasClassLoader(getClassLoader()); 
/* Send optional extras */ 
Bundle bundle = new Bundle(); 
bundle.putParcelable("object", mObject); 
bundle.putString("stringVal", stringVal); 

intent.putExtra("bundle",bundle); 

Và trong lớp ý định của tôi, tôi sử dụng này để lấy giá trị:

Bundle oldBundle = intent.getBundleExtra("bundle"); 
ResultReceiver receiver = oldBundle.getParcelable("object"); 
String stringVal = oldBundle.getString("stringVal"); 
intent.setExtrasClassLoader(getClassLoader()); 

Hy vọng nó sẽ rất hữu ích cho một số.

0

Bạn có thể gặp lỗi này nếu bạn phân lớp Chế độ xem tùy chỉnh không chính xác.

Giả sử bạn đang phân lớp BottomNavigationView và bạn muốn thêm trạng thái đã lưu vào superstate trong onSaveInstanceState().

An thực hiện không chính xác của các soạn sẵn Parcelable (sao chép từ một lớp hay một bản mẫu) sẽ trông như thế này:

static class State extends BaseSavedState { 

    Bundle stateBundle; 

    //incorrect as super state uses ClassLoaderCreator 
    public static final Creator<State> CREATOR = new Creator<State>() { 
     public State createFromParcel(Parcel in) { 
      return new State(in); 
     } 

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

    State(Parcel source) { 
     super(source); 
     this.stateBundle = source.readBundle(getClass().getClassLoader()); 
    } 

    State(Parcelable superState) { 
     super(superState); 
    } 

    @Override 
    public void writeToParcel(Parcel out, int flags) { 
     super.writeToParcel(out, flags); 
     out.writeBundle(stateBundle); 
    } 
} 

Điều này sẽ không làm việc như các superstate từ BottomNavigationView đòi hỏi một classloader. Thay vào đó bạn nên cẩn thận kiểm tra lớp SavedState từ BottomNavigationView và sử dụng đúng ClassLoaderCreator hơn Creator:

static class State extends AbsSavedState { 

    Bundle stateBundle; 

    public static final Creator<State> CREATOR = new ClassLoaderCreator<State>() { 
     public State createFromParcel(Parcel in, ClassLoader classLoader) { 
      return new State(in, classLoader); 
     } 

     @Override 
     public State createFromParcel(Parcel source) { 
      return new State(source, null); 
     } 

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

    State(Parcel source, ClassLoader classLoader) { 
     super(source, classLoader); 
     this.stateBundle = source.readBundle(classLoader); 
    } 

    State(Parcelable superState) { 
     super(superState); 
    } 

    @Override 
    public void writeToParcel(Parcel out, int flags) { 
     super.writeToParcel(out, flags); 
     out.writeBundle(stateBundle); 
    } 
} 

Lưu ý rằng việc mở rộng android.support.v4.view.AbsSavedState có thể là một lựa chọn tốt hơn so với BaseSavedState hoặc android.view.AbsSavedState vì nó sẽ cho phép bạn để vượt qua một bộ nạp lớp để siêu lớp:

SavedState(Parcel source, ClassLoader classLoader) { 
    super(source, classLoader); //available in android.support.v4.view.AbsSavedState 
    this.stateBundle = source.readBundle(classLoader); 
} 
Các vấn đề liên quan