首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >添加到Parcel的Parcelable inside bundle

添加到Parcel的Parcelable inside bundle
EN

Stack Overflow用户
提问于 2012-11-17 01:33:09
回答 4查看 9.6K关注 0票数 11

在我的项目中,我有一个模型,其中包含有关模型的基本信息。例如,假设模型是一辆汽车。然后有许多不同的汽车品种,这些汽车有不同的数据分配给它们。所有型号都必须是可包装的。

不同汽车之间的差异非常小,可能只是几个数据字段。因此,这可以通过为不同的汽车创建presenters (只是一个保存数据的类)来解决。然后,演示者将知道它应该保存哪些额外数据。因为presenter本身是不可打包的,所以它将为所有数据创建一个Bundle,然后Car类会将其添加到parcelable中。我不想让演讲者变成可打包的。

因此,Car从演示者手中接过Bundle,并将其放入其包裹中:

代码语言:javascript
复制
  public void writeToParcel(Parcel parcel, int flags) {
    parcel.writeBundle(getPresenter().getBundle());
  } 

然后,它将使用以下命令解压:

代码语言:javascript
复制
  public Car(Parcel parcel) {    
    getPresenter().setBundle(parcel.readBundle());
  }

这可以很好地工作,直到演示者将可打包对象添加到包中。然后我得到了这个错误:

代码语言:javascript
复制
11-16 15:06:37.255: E/AndroidRuntime(15193): FATAL EXCEPTION: main
11-16 15:06:37.255: E/AndroidRuntime(15193): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.activity}: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.model.engine
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2185)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2210)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.app.ActivityThread.access$600(ActivityThread.java:142)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1208)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Handler.dispatchMessage(Handler.java:99)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Looper.loop(Looper.java:137)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.app.ActivityThread.main(ActivityThread.java:4931)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at java.lang.reflect.Method.invokeNative(Native Method)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at java.lang.reflect.Method.invoke(Method.java:511)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at dalvik.system.NativeStart.main(Native Method)
11-16 15:06:37.255: E/AndroidRuntime(15193): Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.model.engine
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Parcel.readParcelable(Parcel.java:2077)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Parcel.readValue(Parcel.java:1965)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Parcel.readMapInternal(Parcel.java:2226)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Bundle.unparcel(Bundle.java:223)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at android.os.Bundle.getString(Bundle.java:1055)
11-16 15:06:37.255: E/AndroidRuntime(15193):         at com.example.cars.CarPresenter.getExtraString(CarPresenter.java:34)
11-16 15:06:37.255: E/AndroidRuntime(15193):         ... 11 more

因此,它不知何故无法从Bundle中读取任何内容。

这可以通过修改readBundle调用来解决:

代码语言:javascript
复制
  public Car(Parcel parcel) {    
    getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader()));
  }

然而,这是否意味着我的捆绑包中只能有一种类型的包裹?例如,如果另一个演示者想要将另一个可打包对象添加到捆绑包中,该怎么办?

有没有人能解释一下这个问题?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-11-23 08:38:22

你在评论中写道:

然而,我的问题更多的是为什么在这种情况下我必须指定一个类加载器

黛安·哈克伯恩,安卓框架工程师,writes

当捆绑包从包裹中读取时,它只是提取数据。直到稍后从捆绑包中检索内容时,它才真正从数据中解包。

Bundle source code中,我们可以看到setClassLoader()方法设置了字段mClassLoader

代码语言:javascript
复制
public void setClassLoader(ClassLoader loader) {
     mClassLoader = loader;
 }

如果我们不使用setClassLoader()或构造函数Bundle(ClassLoader loader)mClassLoader字段将被设置为默认的ClassLoader

代码语言:javascript
复制
public Bundle(int capacity) {
     //...
     mClassLoader = getClass().getClassLoader();
 }

然后在unparcel()方法中使用mClassLoader对打包后的数据进行解组:

代码语言:javascript
复制
 mParcelledData.readMapInternal(mMap, N, mClassLoader);
 mParcelledData.recycle();
 mParcelledData = null;

Net,如果不设置ClassLoader,那么在解组它的打包数据时,它将默认为系统ClassLoader,并且在解组自定义Parcelable对象数据时,我们将获得一个ClassNotFoundException

票数 15
EN

Stack Overflow用户

发布于 2012-11-27 08:19:47

ClassLoaders是Java中最鲜为人知的特性之一。它们提供“命名空间”,实际上,这些命名空间可以“嵌套”,因为如果当前ClassLoader找不到一个类,它可以检查“父”ClassLoader。

在Android应用程序中,有两个ClassLoaders。其中一个知道如何从APK装入类,另一个知道如何从Android框架装入类。前者将后者设置为“父”类加载器,因此通常您不会注意到。但是,由于Bundle是一个框架类,并且是由框架类加载器加载的,因此您需要告诉它用于在APK中查找类的ClassLoader。Bundle默认使用的类加载器是框架ClassLoader,它的命名空间比包含APK类的命名空间“低”。

因此,通过告诉捆绑包使用哪个ClassLoader,您就是在告诉它需要检查APK ClassLoader中的类。它默认检查框架APK,因为它是加载Bundle类的ClassLoader,因此它是唯一知道如何在其中查找类的“命名空间”。

票数 17
EN

Stack Overflow用户

发布于 2012-11-24 06:57:12

你写道:

可以通过将readBundle调用修改为:

public Car(Parcel parcel) { getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader())); }

然而,这是否意味着我的捆绑包中只能有一种类型的包裹?例如,如果另一个演示者想要将另一个可打包对象添加到捆绑包中,该怎么办?

实际上,不是。当您将classLoader设置为engine.class.getClassLoader()时,您实际上提供了一个知道如何从您的APK加载所有类的类加载器。因此,您可以使用相同的类加载器来解包应用程序中实现Parcelable的所有其他自定义类。

您看到的问题是,当您不指定一个类加载器时,(默认的)系统类加载器被用来对Bundle进行解包。系统类加载器只知道如何加载Android系统已知的类,而不知道如何加载任何特定于应用程序的类。

只是为了澄清,(通常)没有“每个类的类加载器”,有一个系统类加载器,然后有一个“每个应用程序的类加载器”。我希望这能回答你的问题。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13421582

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档