Android 添加到包裹中的包裹内部包裹
在我的项目中,我有一个模型,其中包含了关于该模型的基本信息。例如,假设模型是一辆汽车。然后有许多不同种类的汽车,它们有不同的数据分配给它们。所有模型必须是包裹 不同车辆之间的差异非常小,可能只是几个数据字段。因此,这可以通过为不同的汽车创建演示者(只是一个保存数据的类)来解决。然后,演示者将知道它应该保存哪些额外数据。因为演示者本身不可包裹,所以它将有一个用于其所有数据的捆绑包,然后汽车类将添加到包裹中。我不想把演讲者变成小包裹 因此,Car从演示者处取出包裹并将其放入包裹中:Android 添加到包裹中的包裹内部包裹,android,parcelable,Android,Parcelable,在我的项目中,我有一个模型,其中包含了关于该模型的基本信息。例如,假设模型是一辆汽车。然后有许多不同种类的汽车,它们有不同的数据分配给它们。所有模型必须是包裹 不同车辆之间的差异非常小,可能只是几个数据字段。因此,这可以通过为不同的汽车创建演示者(只是一个保存数据的类)来解决。然后,演示者将知道它应该保存哪些额外数据。因为演示者本身不可包裹,所以它将有一个用于其所有数据的捆绑包,然后汽车类将添加到包裹中。我不想把演讲者变成小包裹 因此,Car从演示者处取出包裹并将其放入包裹中: public
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeBundle(getPresenter().getBundle());
}
然后,它将使用以下内容打开包装:
public Car(Parcel parcel) {
getPresenter().setBundle(parcel.readBundle());
}
在演示者将可包裹对象添加到捆绑包之前,此操作可以正常工作。
然后我得到这个错误:
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
因此,它不知何故无法从包中读取任何内容
可以通过将readBundle调用修改为:
public Car(Parcel parcel) {
getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader()));
}
然而,这不意味着我的包裹里只能有一种类型的包裹吗?例如,如果另一个演示者希望将另一个可包裹对象添加到包中,该怎么办
有人能解释一下吗?这是我的模型,它实现了包裹:
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");
}
}
你在评论中写道: 然而,我的问题是,为什么在这种情况下我甚至必须指定一个类加载器 Android框架工程师Dianne Hackborn表示: 当包读取包裹时,它只提取数据。直到稍后您从捆绑包中检索内容时,它才真正从该数据中分离 在中,我们看到
setClassLoader()
方法设置字段mClassLoader
:
public void setClassLoader(ClassLoader loader) {
mClassLoader = loader;
}
public Bundle(int capacity) {
//...
mClassLoader = getClass().getClassLoader();
}
如果我们不使用setClassLoader()
或构造函数包(ClassLoader)
,则mClassLoader
字段将设置为默认的ClassLoader
:
public void setClassLoader(ClassLoader loader) {
mClassLoader = loader;
}
public Bundle(int capacity) {
//...
mClassLoader = getClass().getClassLoader();
}
然后使用mClassLoader
在unparcel()
方法中解组包裹数据:
mParcelledData.readMapInternal(mMap, N, mClassLoader);
mParcelledData.recycle();
mParcelledData = null;
Net中,如果不设置类加载器
,则在解组其打包数据时,它将默认为系统类加载器
,并且在解组自定义打包对象数据时,我们将得到类NotFoundException
,您写道:
可以通过将readBundle调用修改为:
public Car(Parcel parcel) {
getPresenter().setBundle(parcel.readBundle(engine.class.getClassLoader()));
}
公共汽车(包裹){
getPresenter().setBundle(packet.readBundle(engine.class.getClassLoader());
}
然而,这难道不意味着我只能有一种
我的包裹里有包裹吗?例如,如果另一个演示者
要将另一个可包裹对象添加到捆绑包中吗
实际上,不是。当您将类加载器设置为engine.class.getClassLoader()
时,您实际上提供了一个类加载器,它知道如何从APK加载所有类。因此,您可以使用相同的类加载器来解析应用程序中实现Parcelable的所有其他自定义类
您看到的问题是,当您没有指定类加载器时,(默认)系统类加载器将用于对捆绑包进行解包。系统类加载器只知道如何加载Android系统已知的类,而不知道如何加载任何特定于应用程序的类
只是澄清一下,(通常)没有“每个类的类加载器”,有一个系统类加载器,然后有一个“每个应用程序的类加载器”。我希望这能回答您的问题。类加载器是Java最难理解的特性之一。它们提供“名称空间”,事实上,这些名称空间可以“嵌套”,因为如果当前类加载器找不到类,它可以检查“父”类加载器
对于Android应用程序,有两个类加载器。有一个知道如何从APK加载类,还有一个知道如何从Android框架加载类。前者将后者设置为“父”类加载器,因此通常您不会注意到。然而,由于Bundle是一个框架类,并且由框架类加载器加载,所以您需要告诉它在APK中查找类所使用的类加载器。Bundle默认使用的类加载器是frameworkclassloader,它是一个比包含APK类的名称空间“更低”的名称空间
因此,告诉bundle使用哪个类加载器就是告诉它需要检查APK类加载器中的类。它默认检查框架APK,因为它是加载Bundle类的类加载器,因此是它知道如何在其中查找类的唯一“名称空间”。我知道包裹的基本知识,它在我的应用程序中的其他任何地方都能工作。发动机没有实现可包裹。问题是,如果我将一个包裹放在一个包裹中,然后再将包裹放在一个包裹中,我无法从包裹(或包裹中的任何其他字段)中解包包裹。是的,我尝试过,我认为它不起作用。然而,我的问题是,为什么在这种情况下我甚至必须指定一个类加载器,但你仍然没有回答这个问题。为什么必须指定自定义类加载器,为什么系统类加载器不能加载该类?我有很多实现parcelable的模型。。。这没什么新鲜事。