2013-08-08 6 views
39

사용자 지정 클래스 org.example.app.MyClass implements Parcelable이 주어지면 List<MyClass>을 Parcel에 써야합니다. 내가 "BadParcelableException: ClassNotFoundException when unmarshalling org.example.app.MyClass" 예외가ClassLoader를 인수로 사용하는 Parcel.read 메서드를 사용하는 동안 BadParcelableException : ClassNotFoundException을 역 마샬링 할 때

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

와 클래스를 언 마샬링하려고 할 때마다 나는

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

로 마샬링을했다.

무엇이 잘못 되었나요? 수업을 찾을 수없는 이유는 무엇입니까?

+1

다른 상황에서이 오류가 발생했습니다. Parcelable이 포함 된 번들에서 'bundle.keySet()'을 호출했습니다. 문제의 코드에 대해 개별 테스트 클래스를 실행하면 통과했지만 전체 테스트 스위트를 실행하면 'BadParcelableException'이 발생합니다. "수정"은'bundle.keySet()'전에'bundle.setClassloader (MyClass.class.getClassLoader())'를 수행하는 것이 었습니다. –

답변

84

null을 Classloader 인수로 사용할 때 사용되는 프레임 워크 클래스 로더를 사용하여 애플리케이션이 제공하고 Android 프레임 워크가 아닌 사용자 정의 클래스를 언 마샬 해제하지 마십시오. 응용 프로그램 클래스 로더를 사용 : Parcel.read*() 방법도 인수로 클래스 로더를 가지고 때마다

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

(예 Parcel.readList(List outVal, ClassLoader loader)을) 당신은 Parcel에서 응용 프로그램 클래스를 읽어 getClass().getClassLoader()으로 검색 할 수있는 응용 프로그램 클래스 로더를 사용하려면 .

배경 : Android에는 2 개의 클래스 로더가 제공됩니다. 시스템 클래스 로더는 모든 시스템 클래스를로드 할 수 있지만 응용 프로그램에서 제공하는 클래스는로드 할 수 있습니다. 그리고 응용 프로그램 클래스 로더는 시스템 클래스 로더를 부모로 설정하여 모든 클래스를로드 할 수 있습니다. 클래스 로더를 null로 지정하면 Parcel.readParcelableCreator(), will use the framework class loader이 발생하여 ClassNotFoundException이 발생합니다. 바른 길에 저를 이끌어 the hint를 제공 alexanderblom에

덕분에

16

나는이의 더 정확한 형태가 될 것이다 믿습니다

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

명시 적으로 클래스 로더를 사용하고 여기에 있기 때문에 List의 제네릭 형식

+6

기본적으로 내 대답과 같습니다. MyClass.class와 getClass() 사이에는 차이점이 없으며, getClass()가 MyClass 내에서 호출되기 때문에 둘 다 동일한 클래스 객체를 반환합니다. 또한 사용자 정의 클래스이고 프레임 워크에서 제공하는 클래스가 아닌 한 여기에서 모든 클래스를 사용할 수 있습니다. – Flow

+6

나는 당신이 틀렸다는 말을하지 않았으며,이 양식이 더 적절할 수도 있습니다. 또한 클래스를 정적으로 참조 할 때 불필요한 getClass() 메소드 호출을 추가로 작성한다고 생각하십시오. –

3

저도 같은 문제를 직면하고 대신 parcleable의 나는 번들

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

intent.putExtra("bundle",bundle); 

을 사용 그리고 내 의도 수업 시간에 내가 값을 검색하려면이 옵션을 사용 :

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

그것이 도움이 될 것입니다 희망을 약간.

0

사용자 지정보기를 잘못 하위 클래스로 분류하면이 오류가 발생할 수 있습니다.

서브 클래스를 BottomNavigationView으로하고 슈퍼 상태에 저장된 상태를 onSaveInstanceState()에 추가한다고 가정합니다.

(다른 클래스 또는 템플릿에서 복사)를 Parcelable 보일러의 잘못된 구현은 다음과 같습니다

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

BottomNavigationView에서 초 국가로 작동하지 않을 것입니다은 클래스 로더를 필요로한다. 대신 당신은주의 깊게 BottomNavigationView에서 SavedState 클래스를 검사하고 올바른 ClassLoaderCreator 오히려 Creator보다 사용한다 : 그것은 당신이 클래스 로더를 통과 할 수 있기 때문에 BaseSavedState 또는 android.view.AbsSavedState보다

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

android.support.v4.view.AbsSavedState을 연장 할 수있는 더 나은 선택을 슈퍼 클래스 :

SavedState(Parcel source, ClassLoader classLoader) { 
    super(source, classLoader); //available in android.support.v4.view.AbsSavedState 
    this.stateBundle = source.readBundle(classLoader); 
}