在页面跳转的时候,我们经常使用Intent来传递数据,比如我们要传递一个BundleBean,我们使BundleBean实现Serializable或者Parcelable接口,通过intent.putExtra("data", bean)
即可在页面间传递数据。但是,当BundleBean中有一个Bundle类型的字段时,这种方法就不能正常发挥作用了。
代码如下:
public class BundleBean implements Parcelable {
public int age;
public Bundle bundle;
public BundleBean(int age, Bundle bundle) {
this.age = age;
this.bundle = bundle;
}
protected BundleBean(Parcel in) {
age = in.readInt();
bundle = in.readBundle();
}
public static final Creator<BundleBean> CREATOR = new Creator<BundleBean>() {
@Override
public BundleBean createFromParcel(Parcel in) {
return new BundleBean(in);
}
@Override
public BundleBean[] newArray(int size) {
return new BundleBean[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeBundle(bundle);
}
}
public class SubBean implements Parcelable {
public int a = 1;
public SubBean(int a) {
this.a = a;
}
protected SubBean(Parcel in) {
a = in.readInt();
}
public static final Creator<SubBean> CREATOR = new Creator<SubBean>() {
@Override
public SubBean createFromParcel(Parcel in) {
return new SubBean(in);
}
@Override
public SubBean[] newArray(int size) {
return new SubBean[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
}
}
在第一个界面传递:
Bundle bundle = new Bundle()
bundle.putParcelable("sub", new SubBean(1))
BundleBean bean = BundleBean(10, bundle)
intent.putExtra("data", bean)
intent.setClass(this, BundleActivity.class)
在第二个界面接收:
Intent intent = getIntent();
BundleBean data = intent.getParcelableExtra("data");
SubBean sub = data.bundle.getParcelable("sub");
if(sub != null) {
Log.i("tag", sub.a + "");
}
按理说,我们可以在第二个页面接收到数据了?这样想你就大错特错了,你会发现应用崩了,看一下控制台:
android.os.BadParcelableException:ClassNotFoundException when unmarshalling:jzd.myapplication.bundle.bean.SubBean
这是什么原因呢?自习看一下BundleBean类,这里我用Android Studio自动生成了Parcelable中的方法,IDE在
bundle = in.readBundle();
这行有一个提示Using the default class loader will not work if you are restoring your own classes. Consider using for example readBundle(getClass().getClassLoader()) instead
,意思是说使用默认的ClassLoader不能“复原”BundleBean中bundle字段中存储的数据,解决的关键点当然是ClassLoader类了。
既然我们bundle中存储了个SubBean,我们自然而然的想到,使用SubBean.class.getClassLoader()
来复原里边的数据,我们改造一下BundleBean:将bundle = in.readBundle()
改为bundle = in.readBundle(SubBean.class.getClassLoader());
然后运行一下,发现数据传递成功了。
到此为止,我们成功使用Intent传递了Bundle类型的数据。
我们再深入一下:为什么我们要使用Bundle类型的字段呢,因为我这个BundleBean中可能会存放不同类型的数据,那我们使用in.readBundle(SubBean.class.getClassLoader())
岂不是写死了?如果我使用bundle存放了个User类,岂不是还是传递失败了?那我们怎么解决呢?
我们需要在writeToParcel方法中保存ClassLoader,后面赋值的时候才能使用该ClassLoader:
public class BundleBean implements Parcelable {
public int age;
public Bundle bundle;
public BundleBean(int age, Bundle bundle) {
this.age = age;
this.bundle = bundle;
}
protected BundleBean(Parcel in) {
age = in.readInt();
String name = in.readString();
if(name != null)
{
try {
bundle = in.readBundle(Class.forName(name).getClassLoader());
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static final Creator<BundleBean> CREATOR = new Creator<BundleBean>() {
@Override
public BundleBean createFromParcel(Parcel in) {
return new BundleBean(in);
}
@Override
public BundleBean[] newArray(int size) {
return new BundleBean[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
if(bundle != null) {
Parcelable sub = bundle.getParcelable("sub");
if(sub != null) {
dest.writeString(sub.getClass()
.getName());
dest.writeBundle(bundle);
}
}
//dest.writeBundle(bundle);
}
}
来测试一下:
Bundle bundle = new Bundle();
bundle.putParcelable("sub", new SubBean(1));
bundle.putInt("sub1", 2);
bundle.putParcelable("sub2", new SubBean(3));
bundle.putParcelable("other", new Other("other"));
val bean = BundleBean(10, bundle);
intent.putExtra("data", bean);
intent.setClass(this, BundleActivity::class.java);
Intent intent = getIntent();
BundleBean data = intent.getParcelableExtra("data");
SubBean sub = data.bundle.getParcelable("sub");
if(sub != null) {
Log.i("tag", sub.a + "");
}
int sub1 = data.bundle.getInt("sub1");
Log.i("tag", sub1 + "");
SubBean sub2 = data.bundle.getParcelable("sub2");
if(sub2 != null) {
Log.i("tag", sub2.a + "");
}
Other other = data.bundle.getParcelable("other");
if(other != null) {
Log.i("tag", other.name);
}
输出:
1
2
3
other
至此,我们的BundleBean中就可以存放任意类型的数据了。