2012-11-16 84 views
8

在我的项目中,我有一个包含有关模型的基本信息的模型。例如,可以说模型是一辆汽车。然后有许多不同的汽车品种,这些汽车有不同的数据分配给他们。所有模型必须是可接受的。添加到包裹中的可编辑的内部包裹

不同车型之间的差异很小,可能只是一些数据领域。所以这可以通过为不同的汽车创建演示者(只是一个保存数据的类)来解决。演示者会知道应该保存哪些额外的数据。因为演示者本身不可分类,所以它的所有数据都会有一个Bundle,然后Car类将添加到可分类中。我不想让演示者变成可以包装的东西。

因此汽车从演讲者采取捆绑,并把它放在其包裹:

public void writeToParcel(Parcel parcel, int flags) { 
    parcel.writeBundle(getPresenter().getBundle()); 
    } 

,然后它会用它解:

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

这工作得很好,直到parcelable对象添加由演示者提供给包。 后来我得到这个错误:

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 

所以它在某种程度上无法读取从包东西。

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

然而,就不是这个意思,我只能有一种类型,我捆绑Parcelables并的:

这可以通过修改readBundle呼叫被解决了吗?例如,如果另一个演示者想要将另一个可分段对象添加到该包中呢?

任何人都可以对此有所了解吗?

回答

15

你在写评论:

However, my question was ment to be more of why do I even have to specify a class loader in this case

戴安娜Hackborn,Android框架工程师,writes是:

when the bundle reads from the parcel it just extracts the data. It doesn't actually unparcel from that data until later when you retrieve stuff from the bundle.

Bundle source code,我们看到setClassLoader()方法将字段设置mClassLoader

public void setClassLoader(ClassLoader loader) { 
    mClassLoader = loader; 
} 

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

public Bundle(int capacity) { 
    //... 
    mClassLoader = getClass().getClassLoader(); 
} 

mClassLoader然后用于解组在unparcel()方法瓜分数据:

mParcelledData.readMapInternal(mMap, N, mClassLoader); 
mParcelledData.recycle(); 
mParcelledData = null; 

网,如果你不设置ClassLoader它会默认为系统的解组数据包包的时候ClassLoader,和我们的解组自定义Parcelable对象数据的时候,我们会得到一个ClassNotFoundException

+0

我知道包裹的基础,并在我的应用程序工作在其他地方。引擎确实实现了Parcelable。问题是,如果我将一个parcelable放入一个包中,然后将这个包放入一个parcel中,那么我无法解压该包(或bundle中的任何其他字段)的可放入表单。 – Heinrisch

+0

是的,我已经尝试过,我认为它没有工作。然而,我的问题是,为什么我甚至不得不在这种情况下指定一个类加载器。 – Heinrisch

+0

我已经更新了我的答案。 –

-3

这是我的模型,实现Parcelable:

public class ProgramModel implements Parcelable 
{ 
    private int id = -1; 
    private String title = ""; 
    private String time = ""; 
    private String day = ""; 
    private String organization = ""; 
    private String participants = ""; 
    private String description = ""; 
    private String descriptionFull = ""; 
    private String location = ""; 
    private String locationCaption = ""; 
    private int locationCode = 0; 
    private String locationMap = ""; 
    private String imageUrl = ""; 
    private String color = ""; 
    private int colorId = -1; 
    private int columnId = -1; 
    private String startTime = ""; 
    private String endTime = ""; 
    private Date convertedTimeStart = null; 
    private Date convertedTimeEnd = null; 
    private String theme = ""; 
    private ArrayList<TagModel> tags = new ArrayList<TagModel>(); 
    private ArrayList<Integer> tagsId = new ArrayList<Integer>(); 

    public ProgramModel() 
    { 
    } 

    private ProgramModel(Parcel in) 
    { 
     id = in.readInt(); 
     title = in.readString(); 
     time = in.readString(); 
     day = in.readString(); 
     organization = in.readString(); 
     participants = in.readString(); 
     description = in.readString(); 
     descriptionFull = in.readString(); 
     location = in.readString(); 
     locationCaption = in.readString(); 
     locationCode = in.readInt(); 
     locationMap = in.readString(); 
     imageUrl = in.readString(); 
     color = in.readString(); 
     colorId = in.readInt(); 
     columnId = in.readInt(); 
     startTime = in.readString(); 
     endTime = in.readString(); 
     convertedTimeStart = new Date(in.readLong()); 
     convertedTimeEnd = new Date(in.readLong()); 
     theme = in.readString(); 
     in.readTypedList(tags, TagModel.CREATOR); 
     in.readList(tagsId, Integer.class.getClassLoader()); 
    } 

    public int getId() 
    { 
     return id; 
    } 

    public void setId(int id) 
    { 
     this.id = id; 
    } 

    // Other getters and setters 

    @Override 
    public int describeContents() 
    { 
     return 0; 
    } 

    // this is used to regenerate your object 
    public static final Parcelable.Creator<ProgramModel> CREATOR = new Parcelable.Creator<ProgramModel>() 
    { 
     public ProgramModel createFromParcel(Parcel in) 
     { 
      return new ProgramModel(in); 
     } 

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

    @Override 
    public void writeToParcel(Parcel out, int flags) 
    { 
     out.writeInt(id); 
     out.writeString(title); 
     out.writeString(time); 
     out.writeString(day); 
     out.writeString(organization); 
     out.writeString(participants); 
     out.writeString(description); 
     out.writeString(descriptionFull); 
     out.writeString(location); 
     out.writeString(locationCaption); 
     out.writeInt(locationCode); 
     out.writeString(locationMap); 
     out.writeString(imageUrl); 
     out.writeString(color); 
     out.writeInt(colorId); 
     out.writeInt(columnId); 
     out.writeString(startTime); 
     out.writeString(endTime); 
     out.writeLong(convertedTimeStart.getTime()); 
     out.writeLong(convertedTimeEnd.getTime()); 
     out.writeString(theme); 
     out.writeTypedList(tags); 
     out.writeList(tagsId); 
    } 
} 

将数据写入到意向中的活动:从捆在活动

Intent intent = new Intent(FirstActivity.this, SecondActivity.class); 
intent.putExtra("ProgramModel", programDetailsModel); 
startActivity(intent); 

读取数据:

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    savedInstanceState = getIntent().getExtras(); 
    if (savedInstanceState != null) 
    { 
     fromBundleModel = savedInstanceState.getParcelable("ProgramModel"); 
    } 
} 
+1

我有很多的车型,它实现parcelable ......这没有新的东西给我。 – Heinrisch

3

您写道:

This can be solved by modifying the readBundle call to:

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

However, wouldn't this mean that I could only have one type of parcelables in my bundle? For example, what if another presenter wanted to add another parcelable object to the bundle?

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

你所看到的问题是,当你不要指定一个类加载器,(默认)系统类加载器用于undearcel Bundle。系统类加载器只知道如何加载Android系统已知的类,而不知道如何加载任何特定于应用程序的类。

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

12

ClassLoaders是Java最少理解的功能之一。它们提供了“命名空间”,实际上这些命名空间可以“嵌套”,因为如果一个类没有被当前的ClassLoader找到,它可以检查一个“Parent”ClassLoader。

在Android应用程序中,有两个类加载器。有一个知道如何从你的APK加载类以及知道如何从Android框架加载类的类。前者将后者设置为“父”类加载器,因此一般情况下您都没有注意到。然而,由于包是一个框架类,并且是由框架类加载器加载,你需要告诉它的类加载器被用来找到你的APK类。该包默认使用的类加载器是框架的ClassLoader这是一个“低”的命名空间比一个在它的APK类。

所以告诉类加载器使用哪个你告诉它,它需要检查APK的ClassLoader的类的包。它默认为选中框架APK,因为这是其加载的捆绑类的类加载器,因此是唯一的“命名空间”它知道如何找到类。