BadParcelableException: 在解组时找不到类的异常

9

我对Serializable和Parcelable还不太熟悉。我现在正在尝试将一个对象的实例从应用程序传递到远程服务,但遇到了困难:

public class Event implements Parcelable, Cloneable {

    /** Defines under what Bundle's key architecture events are stored */ 
    public static final String BUNDLE_KEY = "ZKEvent";

    /** Defines which messages are architecture's events */
    public static final int MSG_WHAT = 0xDEFECABE;

    /** Defines a key to store map under a Bundle to put into a Parcel (oh yeah!) */
    private static final String MAP_KEY = "MAP";

    /** Indicates this event ID. This ID should be the same in the event answer if any */
    private int idEvent = 0;

    /** Indicates this event priority */
    private int priority = EventPriority.EVENT_PRIORITY_LOW;

    /** ID for this event's sender */
    private int idSource = 0;

    /** ID for this event's destination */
    private int idDestination = 0;

    /** Action to be performed */
    private int action = EventAction.DEFAULT;

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

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

    /** 
     * Event's data
     * Represented as 
     *      key -> parameter;
     *      value -> data 
     */
    private Map<String, Object> data = new HashMap<String, Object>();

    /** Default constructor */
    public Event() {};

    /** Contructor with ID of the event */
    public Event(int id) {
        idEvent = id;
    }

    /** Constructor for Parcelable */
    public Event(Parcel in) {
        readFromParcel(in);
    }

    /** Gets event's priority */
    public int getPriority() {
        return priority;
    }

    /** Sets this event's priority */
    public void setPriority(int priority) {
        this.priority = priority;
    }

    /** Gets the ID of this event's sender */   
    public int getIdSource() {
        return idSource;
    }

    /** Sets the ID of this event's sender */
    public void setIdSource(int idSource) {
        this.idSource = idSource;
    }

    /** Gets the ID of this event's destination */
    public Integer getIdDestination() {
        return idDestination;
    }

    /** Sets the ID of this event's destination */
    public void setIdDestination(Integer idDestination) {
        this.idDestination = idDestination;
    }

    /** Gets the action of this event */
    public int getAction() {
        return action;
    }

    /** Sets the action of this event */
    public void setAction(int action) {
        this.action = action;
    }

    /** Gets the data of this event */
    public Object getData(String key) {
        return data.get(key);
    }

    /** Sets the data of this event  */
    public void addData(String key, Object data) {
        this.data.put(key, data);
    }

    /** Gets the ID of this event */
    public int getIdEvent() {
        return idEvent;
    }

    /** Sets the ID of this event */
    public void setIdEvent(int idEvent) {
        this.idEvent = idEvent;
    }

    @Override
    public Object clone() {

        Event clone = new Event();

        clone.action = this.action;
        clone.idDestination = this.idDestination;
        clone.idEvent = this.idEvent;
        clone.idSource = this.idSource;
        clone.priority = this.priority;

        for(Entry<String,Object> entry: this.data.entrySet()) {
            clone.data.put(entry.getKey(), entry.getValue());
        }

        return clone;
    }

    @Override
    public int describeContents() {
        // TODO Nothing here
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {

        out.writeInt(idEvent);
        out.writeInt(priority);
        out.writeInt(idSource);
        out.writeInt(idDestination);
        out.writeInt(action);

        Bundle mapBundle = new Bundle();
        mapBundle.putSerializable(MAP_KEY, (Serializable) data);
    out.writeBundle(mapBundle);
    }

    /** Restoring this object from a Parcel */
    public void readFromParcel(Parcel in) {

        idEvent = in.readInt();
        priority = in.readInt();
        idSource = in.readInt();
        idDestination = in.readInt();
        action = in.readInt();

        Bundle mapBundle = in.readBundle();
        data = (Map<String, Object>) mapBundle.getSerializable(MAP_KEY);
    }
}

所以我创建了这个事件类的新实例并将其传递给服务,如下所示:

/** Register with service */
private void registerWithService() {
    Event registerEvent = EventFactory.getEvent();
    registerEvent.setAction(EventAction.REGISTER);
    registerEvent.setIdSource(1);

    Bundle data = new Bundle();
    data.putParcelable(Event.BUNDLE_KEY, registerEvent);

    Message msg = new Message();
    msg.setData(data); // Exception thrown here in the Service
    msg.what = Event.MSG_WHAT;
    try {
        outMessenger.send(msg);
    } catch (RemoteException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

这里是远程服务抛出异常的地方:

    public void handleMessage(Message msg) {
        if (msg.what == Event.MSG_WHAT) {
            // Get the data bundle
            Bundle bundle = msg.getData();
            // Extract the event from the message
            Event event = (Event) bundle.get(Event.BUNDLE_KEY); // Exception thrown here
            sendEvent(event);
        } else {
            Log.i(TAG, "Received non architecture message, dropping...");
        }
    }

并且堆栈跟踪:

07-09 12:38:10.947: E/AndroidRuntime(2234): FATAL EXCEPTION: main
07-09 12:38:10.947: E/AndroidRuntime(2234): android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.blabla.android.core.event.Event
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Parcel.readParcelable(Parcel.java:1958)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Parcel.readValue(Parcel.java:1846)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Parcel.readMapInternal(Parcel.java:2083)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Bundle.unparcel(Bundle.java:208)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Bundle.get(Bundle.java:260)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at com.blabla.android.core.event.EventManager$EventHandler.handleMessage(EventManager.java:44)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.os.Looper.loop(Looper.java:123)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at android.app.ActivityThread.main(ActivityThread.java:3683)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at java.lang.reflect.Method.invokeNative(Native Method)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at java.lang.reflect.Method.invoke(Method.java:507)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-09 12:38:10.947: E/AndroidRuntime(2234):     at dalvik.system.NativeStart.main(Native Method)

任何帮助都非常感激,谢谢!

https://dev59.com/U3I-5IYBdhLWcg3wJE0K#3806769 - Akram
您的服务是否在与发送给它的Bundle的创建者相同的进程中运行,还是这是一个跨进程请求? - Jules
@Akki:我看了那个答案,但是我不知道以后该怎么处理那个 ClassLoader…如果那个类在两个应用程序中都被包含了,为什么我还需要一个单独的 ClassLoader 呢?@Jules:它是一个远程服务,因此它是一个 IPC。 - m0skit0
可能是重复的问题:在使用带有类加载器参数的Parcel.read方法时出现“BadParcelableException: ClassNotFoundException when unmarshalling <myclass>”异常。详见链接:https://dev59.com/ZWMl5IYBdhLWcg3wyJfz - Flow
@Flow 你的意思是另一个问题与这个重复了吗? - m0skit0
2个回答

16
在这个方法中,您不必将Bundle对象写入Parcel中:
public void writeToParcel(Parcel out, int flags) {

    out.writeInt(idEvent);
    out.writeInt(priority);
    out.writeInt(idSource);
    out.writeInt(idDestination);
    out.writeInt(action);

    Bundle mapBundle = new Bundle();
    mapBundle.putSerializable(MAP_KEY, (Serializable) data);
   // You need to actually write the Bundle to the Parcel here...
}

编辑:为ClassLoader问题添加了另一种解决方案:

在您的远程服务中,像这样在handleMessage()中设置类加载器:

Bundle bundle = msg.getData();
bundle.setClassLoader(Event.class.getClassLoader());
Event event = (Event) bundle.get(Event.BUNDLE_KEY);

1
尝试这样做:在远程服务的handleMessage()中设置类加载器,像这样:Bundle bundle = msg.getData(); bundle.setClassLoader(Event.class.getClassLoader()); Event event = (Event) bundle.get(Event.BUNDLE_KEY); - David Wasser
1
好的,我不太清楚为什么你需要在这种情况下设置ClassLoader。显然,默认设置的那个无法看到你的类(它可能是系统ClassLoader,因此只能看到标准的Android内容)。我会进行更多的研究,看看能否想出解决方案。这是一个有趣的问题... - David Wasser
4
getClassLoader()点赞。它帮助我通过将这个代码从 parcel.readSparseArray(null); 改变为 parcel.readSparseArray(MyClass.class.getClassLoader());,来解决BadParcelableException问题。 - kris larson
1
使用Serializable并不是解决Parcable无法工作的方法,它只是一个权宜之计。Parcable比Serializable快得多,这就是Parcable存在的原因! - Roel
1
@Roel 你没有理解重点。整个讨论中都没有提到使用Serializable代替Parcelable的问题。如果你真正看一下原始帖子,你会发现OP正在使用Parcelable并且由于一些愚蠢的错误和类加载器问题而遇到了问题。 - David Wasser
显示剩余11条评论

0

目前找到的唯一解决方案:

 val bundle = Bundle().apply {
        classLoader = YourClassName::class.java.classLoader
        putParcelable("deepLinkData", YourClassName(param1, param2, param3))
 }

请注意,deepLinkData是一个关键字,可以是任何内容。
    val myIntent = Intent(Intent.ACTION_VIEW)
    myIntent.putExtra("deepLinkBundle", bundle)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接