Skip to content

Android Activity 传递Parcelable对象

Android 教程 2020

前面我们知道了启动activity的时候可以传递一些参数。 Activity的跳转时可以传递Parcelable对象。

Parcelable对象和Serializable不一样。实现了Parcelable接口的类并不会被系统序列化。 接下来我们用一个例子看看如何使用这一接口。

数据准备

先设计一个类实现Parcelable接口。前面我们使用Serializable的时候,类只要实现Serializable接口即可,不需要额外的操作。但用Parcelable接口会需要开发者多做一些工作。

Parcelable接口在android.os包里,与Serializable不同。我们必须明确认识这一点。 官方给出的一个使用例子。

public class MyParcelable implements Parcelable {
      private int mData;

      public int describeContents() {
          return 0;
      }

      public void writeToParcel(Parcel out, int flags) {
          out.writeInt(mData);
      }

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

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

      private MyParcelable(Parcel in) {
          mData = in.readInt();
      }
  }

可以看到,强制使用了一个Parcelable.Creator对象。里面的方法我们暂时不管也不修改。 重点关注私有构造器MyParcelable(Parcel inwriteToParcel方法。 官方例子中,私有构造器接收一个Parcel对象,然后从中读出一个int。 而writeToParcel方法中,把mData写入Parcel对象中。 这写入和读出操作就是我们开发者需要特别关心的地方。

之前使用intent.putExtra方法的时候会传入一个String类型的key(键),用于标示传入的数据。 但在官方的例子中,我们只看到了一个int。而writeInt方法也没有指定key。系统如何区分出各个参数呢?

我们自定义一个类DataParcel,实现Parcelable接口。as自动在里面生成了CREATOR。

import android.os.Parcel;
import android.os.Parcelable;

public class DataParcel implements Parcelable {
    private int number;
    private String str1;
    private String str2;
    private String noSave = "[不传送的字符串]";

    // getter setter ...

    public String info() {
        return "number: " + number + ", str1: " + str1 + ", str2: " + str2 + ", noSave: " + noSave;
    }

    protected DataParcel(Parcel in) {
        number = in.readInt();
        str1 = in.readString();
        str2 = in.readString();
    }

    public DataParcel(int number, String str1, String str2, String noSave) {
        this.number = number;
        this.str1 = str1;
        this.str2 = str2;
        this.noSave = noSave;
    }

    public static final Creator<DataParcel> CREATOR = new Creator<DataParcel>() {
        @Override
        public DataParcel createFromParcel(Parcel in) {
            return new DataParcel(in);
        }

        @Override
        public DataParcel[] newArray(int size) {
            return new DataParcel[size];
        }

    };

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(number);
        dest.writeString(str1);
        dest.writeString(str2);
    }
}

可以看到我们有1个int和3个String。公开构造器需要传入这4个属性。 writeToParcel方法中,按顺序写入了int和2个String。 私有构造器中,按顺序读出了int和2个String。

noSave并没有被写入和读出。拿来做对比。 info()方法是拿来打印信息的。

传送Parcelable对象

现在我们的类已经设计好了,传送对象试试。

把DataParcel对象交给intent。

    DataParcel dataParcel = new DataParcel(100, "s1", "s2", "改变这个字符串看看能否被传递");
    intent.putExtra(SendParamsDemo.K_PARCEL, dataParcel);

被打开的Activity接收传入的对象。

DataParcel dataParcel = intent.getParcelableExtra(K_PARCEL);

log中打印出发送和传入的对象信息。

D/rustAppMainActivity: goSendParamsDemo: parcel obj: com.rustfisher.tutorial2020.act.DataParcel@d8ce985
D/rustAppMainActivity: goSendParamsDemo: parcel obj: number: 100, str1: s1, str2: s2, noSave: 改变这个字符串看看能否被传递

D/rustAppSendParamsDemo: gotInput: parcel obj: com.rustfisher.tutorial2020.act.DataParcel@d90a3a6
D/rustAppSendParamsDemo: gotInput: number: 100, str1: s1, str2: s2, noSave: [不传送的字符串]

从log中我们可以看出,发送的对象和接收到的对象并不是同一个对象。 但我们指定的那3个属性是相同的。

至此,我们了解了如何使用Parcelable这个接口。 Parcel和Parcelable是Android IPC中使用到的容器和工具。大家可以了解一下Binder机制。 一般认为,普通情况下Parcelable性能上会优于Serializable。 Serializable涉及到序列化,系统会通过反射的方法来获取信息。相对而言比较耗资源。 Parcel并不涉及序列化机制。它是为了高性能IPC传输设计的。因此,Parcel并不适合用来永久化存储数据。

实际工作中,我们可以根据业务需要,综合开发时间成本和应用性能要求,来选择使用Parcelable或者Serializable。

工程放这里: https://github.com/AnRFDev/Tutorial2020