IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android序列化(二) 之 Parcelable -> 正文阅读

[移动开发]Android序列化(二) 之 Parcelable

1 简介

在Android组件间或者跨进程组件间要传递数据都是通过使用 Intent.putExtra() 或者 Bundle.putXXXExtra()方法进行,这些方法无法支持传递对象的引用,而只支持基本平台类型和实现了Serializable或者Parcelable接口的类对象。

Serializable接口我们在《Android序列化(一) 之 Serializable》中已经介绍过,它是Java提供的可将对象序列化成字节流进行文件持久化存储或网络传输,也可将字节流反序列化成新的对象。

Parcelable接口在android.os包中,实现它便能让对象支持Parcel。简单地理解就是实现Parcelable接口便能使一个自定义类在Android组件间传递具有序列化和反序列化的能力。

Parcel类也在android.os包中,它不是通用序列化机制,而是一种可通过IBinder高性能传输数据和对象的容器,或者说是它是Android里特殊序列化机制。Parcel内部采用共享内存的方式实现用户空间和内核空间的数据交换,本质是Native层的共享内存。

凡通过Parcel包装后的数据都可以通过在Binder进程间通信IPC中进行端和端的数据交互,一端将数据Parcel化后写入到一个共享内存中,另一端通过Parcel可以从这块共享内存中读出字节流还原成原来的数据。Parcel支持基础平台数据类型,也支持实现了Parcelable接口的对象,也支持一个活动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个IBinder的代理IBinder。在Android里AIDL也用到了Parcel进行数据封装。

2 示例

public class Student implements Parcelable {
    private int id;
    private String name;
    private boolean verified;
    private Phone mainPhone;
    private List<Phone> phones;
    private Map<Integer, String> tags;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setVerified(boolean verified) {
        this.verified = verified;
    }
    public boolean isVerified() {
        return verified;
    }
    public Phone getMainPhone() {
        return mainPhone;
    }
    public void setMainPhone(Phone phone) {
        this.mainPhone = phone;
    }
    public List<Phone> getPhones() {
        return phones;
    }
    public void setPhones(List<Phone> phones) {
        this.phones = phones;
    }
    public Map<Integer, String> getTags() {
        return tags;
    }
    public void setTags(Map<Integer, String> tags) {
        this.tags = tags;
    }

    public Student() {
    }
    private Student(Parcel in) {
        id = in.readInt();
        name = in.readString();
        verified = in.readByte() == 1; // API level 29后可以直接使用 readBoolean 支持 Boolean类型
        mainPhone = in.readParcelable(Phone.class.getClassLoader());
        phones = in.readArrayList(Phone.class.getClassLoader());

        // 另外一种方式读取List,需要List先初始化
        // phones = new ArrayList<>();
        // in.readList(phones, Phone.class.getClassLoader());

        // 另外一种方式读取List,但是需要配合 write 时使用了 writeTypedList,这里才应该使用 createTypedArrayList 传入 CREATOR
        // phones = in.createTypedArrayList(Phone.CREATOR);

        // 另外一种方式读取List,但是需要配合 write 时使用了 writeTypedList,这里才应该使用 readTypedList 传入 CREATOR
        // 而且需要List先初始化
        // phones = new ArrayList<>();
        // in.readTypedList(phones, Phone.CREATOR);

        // 如果Map内是基本数据类型,传入的ClassLoader可为null,
        // 如果key或value其中一个或两个都是同一个实现Parcelable的类,传入对应类的ClassLoader,
        // 如果key和value两个是不同的类,传什么都不对
        tags = in.readHashMap(null);
    }
@Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
        dest.writeByte((byte)(verified? 1: 0)); // API level 29后可以直接使用 writeBoolean 支持 Boolean类型
        dest.writeParcelable(mainPhone, 0);
        dest.writeList(phones);
        // 另一种序列化方式,反序列化时需要使用 createTypedArrayList 或 readTypedList
        // dest.writeTypedList(phones);
        dest.writeMap(tags);
    }

    public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

    static class Phone implements Parcelable {
        private String number;
        private String type;
        public String getNumber() {
            return number;
        }
        public void setNumber(String number) {
            this.number = number;
        }
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
        public Phone() {
        }
        private Phone(Parcel in) {
            number = in.readString();
            type = in.readString();
        }
        @Override
        public int describeContents() {
            return 0;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(number);
            dest.writeString(type);
        }
        public static final Parcelable.Creator<Phone> CREATOR = new Parcelable.Creator<Phone>() {
            public Phone createFromParcel(Parcel in) {
                return new Phone(in);
            }
            public Phone[] newArray(int size) {
                return new Phone[size];
            }
        };
    }
}

数据发送方:

Student student = new Student();
student.setId(1001);
student.setName("子云心");
student.setVerified(true);

Student.Phone mainPhone = new Student.Phone();
mainPhone.setType("MAIN");
mainPhone.setNumber("99999999999");
student.setMainPhone(mainPhone);

List<Student.Phone> phones = new ArrayList<>();
Student.Phone phone1 = new Student.Phone();
phone1.setType("MOBILE");
phone1.setNumber("12345678910");
phones.add(phone1);
Student.Phone phone2 = new Student.Phone();
phone2.setType("HOME");
phone2.setNumber("0000-1234567");
phones.add(phone2);
student.setPhones(phones);

Map<Integer, String> tags = new HashMap();
tags.put(0, "三好学生");
tags.put(1, "体育达人");
student.setTags(tags);

Intent intent = new Intent(MainActivity.this, MainActivity2.class);
intent.putExtra("studentData", student);

 // 使用 Bundle 也可以,intent.putExtra内部也是使用了Bundle
 Bundle args = new Bundle();
 args.putParcelable("studentData2", student);
 intent.putExtra("bundleData", args);

startActivity(intent);

数据接收方:

Student student = getIntent().getParcelableExtra("studentData");
Bundle bundle = getIntent().getBundleExtra("bundleData");
Student student2 = bundle.getParcelable("studentData2");

3 类的结构

实现Parcelable接口的类,必须要重写describeContents 和 writeToParcel 两个方法,必须有一个参数是Parcel类型的构造方法,以及必须要声明一个非空的、名字必须是CREATOR的、类型是Parcelable.Creator<T>的静态变量。

3.1 describeContents方法

目前describeContents方法的返回值要么是0,要么是Parcelable.CONTENTS_FILE_DESCRIPTOR(0x0001)。Parcelable.CONTENTS_FILE_DESCRIPTOR表示Paracelable里面含一些特殊的文件描述信息。在大多数情况下返回0即可。

3.2 writeToParcel方法 和 带Parcel参数的构造方法

writeToParcel方法是实现序列化写操作的地方,它返回一个Parcel对象。带Parcel参数的构造方法是实现反序列化读操作的地方,它接收一个Parcel对象。Parcel对象的写入和读取的顺序必须保持一致,否则会发生数值错误或者类型异常崩溃情况。序列化和反序列化由一系列Parcel的write和read方法支持,例如常用的方法有:

byte类型

序列化方法:???????? void writeByte(byte val)

反序列化方法:???? byte readByte()

boolean类型API level 29后才支持,在之前可以使用byte1或者0来代替,如上面示例)

序列化方法:???????? void writeBoolean(boolean val)

反序列化方法:???? boolean readBoolean()

int类型

序列化方法:???????? void writeInt(int val)

反序列化方法:???? int readInt()

String类型

序列化方法:???????? void writeString(String val)

反序列化方法:???? String readString()??

等等一系列基础平台类型

序列化方法:???????? void writeXXX(XXX val)

反序列化方法:???? XXX readXXX()

实现了Parcelable的类

序列化方法:???????? void writeParcelable(@Nullable Parcelable p, int parcelableFlags)

反序列化方法:???? <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader)

Map<K, V>类型

序列化方法:???????? void writeMap(@Nullable Map val)

反序列化方法1,内部会new一个新的HashMap对象:

HashMap readHashMap(@Nullable ClassLoader loader)

反序列化方法2,内部不会new出新对象,输入的outVal必须提前初始化:

void readMap(@NonNull Map outVal, @Nullable ClassLoader loader)?

List<E>类型(使用 ClassLoader 序列化和反序列化)

序列化方法:???????? void writeList(@Nullable List val)

反序列化方法1,内部会new一个新的ArrayList对象:

ArrayList readArrayList(@Nullable ClassLoader loader)

反序列化方法2,内部不会new出新对象,输入的outVal必须提前初始化:

void readList(@NonNull List outVal, @Nullable ClassLoader loader)

List<E>类型(使用Parcelable.Creator序列化和反序列化)

序列化方法:???????? <T extends Parcelable> void writeTypedList(@Nullable List<T> val)

反序列化方法1,内部会new一个新的ArrayList对象:

<T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c)? ? ??

反序列化方法2,内部不会new出新对象,输入的outVal必须提前初始化:

<T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c)

3.3 CREATOR变量

CREATOR变量用于从Parcel中取出指定的数据类型,它是一个非空的、名字必须是CREATOR的、类型是Parcelable.Creator<T>接口的静态变量,接口内有两个方法,其中createFromParcel方法返回的便是反序列化时调用到的带Parcel参数的构造方法。

4 startActivity通过Intent传递参数过程

根据上面示例得知,可以通过Intent.putExtra或者Bundle.putParcelable将实现了Parcelable接口的对象添加到Intent中,从而进行组件间数据传递。Intent.putExtra其实也是调用了Bundle.putBundle。

Intent.java

public @NonNull Intent putExtra(String name, @Nullable Bundle value) {
    if (mExtras == null) {
        mExtras = new Bundle();
    }
    mExtras.putBundle(name, value);
    return this;
}

Bundle.java

public void putBundle(@Nullable String key, @Nullable Bundle value) {
??? unparcel();
??? mMap.put(key, value);
}

public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
??? unparcel();
??? mMap.put(key, value);
??? mFlags &= ~FLAG_HAS_FDS_KNOWN;
}

无论是Bundle.putBundle还是Bundle.putParcelable,都是先调用父类的unparcel方法。

BaseBundle.java

void unparcel() {
    synchronized (this) {
        final Parcel source = mParcelledData;
        if (source != null) {
            initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
        } else {
            if (DEBUG) {
                Log.d(TAG, "unparcel "
                        + Integer.toHexString(System.identityHashCode(this))
                        + ": no parcelled data");
            }
        }
    }
}

unparcel方法内,此时mParcelledData对象还是为null状态mParcelledData下面会进行介绍),所以unparcel方法此时如果是DEBUG则只是打印了一行日志而已。

在调用unparcel方法后putBundle和putParcelable都是将对象添加到一个ArrayMap<String, Object>中去,而Bundle对象最后会保存于Intent中的mExtras变量中。

在进行startActivity时就会将Intent对象传入,startActivity实质是会进行一次AMS(Android10以下是ActivityManagerService/AMS,Android10之后是ActivityTaskManagerService/ATMS)的跨进程通信,这时就会将Intent内的数据也一同传递过去,

Activity.java

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
    ……
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
……
}

Instrumentation.java(源码基于Android10,使用的是ATMS

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
    ……
    int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getBasePackageName(), who.getAttributionTag(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
    ……
}

ActivityTaskManager.java

public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() {
    @Override
    protected IActivityTaskManager create() {
        final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
        return IActivityTaskManager.Stub.asInterface(b);
    }
};

ActivityTaskManager.getService()返回的是实现了IActivityTaskManager接口的ActivityTaskManagerService。IActivityTaskManager位于android/app/IActivityTaskManager.aidl,它没有对应的Java代码,它是一个AIDL文件,返回的结果是 Binder 的代理类 , 该类主要作用是使用了Binder机制 , 进行进程间通信。

IActivityTaskManager生成的Java源码我们无法查看到,但能想像到进行IPC时传入的Intent肯定是先进行序列化的,所以我们可以进行模拟创建一个AILD并在接口方法参数传入一个Intent对象。如:

自定义的IActivityTaskManager.aidl,它是一段模拟IActivityTaskManager的伪代码

package com.zyx.myapplication;
interface IActivityTaskManager {
??? int startActivity(in Intent intent);
}

使用AndroidStudio编译自动生成IActivityTaskManager.java

public interface IActivityTaskManager extends android.os.IInterface {
  ……
  public static abstract class Stub extends android.os.Binder implements com.zyx.myapplication.IActivityTaskManager {
    ……
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code) {
        case INTERFACE_TRANSACTION: {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_startActivity: {
          data.enforceInterface(descriptor);
          android.content.Intent _arg0;
          if ((0!=data.readInt())) {
            // 关键代码3,这里传入序列化后的数据创建一个新的Inetnt对象
            _arg0 = android.content.Intent.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          int _result = this.startActivity(_arg0);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        default: {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.zyx.myapplication.IActivityTaskManager {
      ……
      private android.os.IBinder mRemote;
      @Override public int startActivity(android.content.Intent intent) throws android.os.RemoteException {
        // 关键代码1,创建2个Parcel对象,用于记录序列化数据和序列化结果
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((intent!=null)) {
            _data.writeInt(1);
            // 关键代码2,这里进行了序列化的操作 
            intent.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_startActivity, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().startActivity(intent);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      ……
    }
    ……
  }
  ……
}

从生成的代码可见:

关键代码1中,IActivityTaskManager$Stub$Proxy#startActivity方法开始先通过Parcel的obtain方法先创建两个Parcel对象,用于后面记录序列化数据和序列化结果。

关键代码2中,IActivityTaskManager$Stub$Proxy#startActivity方法传入的Intent参数进行调用Intent的writeToParcel方法,该方法就是序列化的开始(下面会介绍内在过程),序列化的结果通过_data输出,最后通过mRemote.transact将序列化后的_data传递到ActivityTaskManagerService进程去。

从AIDL的运行原理得知,当Client端发起跨进程请求时,远程请求会通过系统底层封装后交由onTransact方法来处理。onTransact方法判断传入的code,即在关键代码2后传入的 TRANSACTION_startActivity 找到关健代码3的地方,关键代码3中,传入将序列化后的数据通过 Intent.CREATOR.createFromParcel创建一个新的Inetnt对象。

Intent.java

public static final @android.annotation.NonNull Parcelable.Creator<Intent> CREATOR
        = new Parcelable.Creator<Intent>() {
    public Intent createFromParcel(Parcel in) {
        return new Intent(in);
    }
    public Intent[] newArray(int size) {
        return new Intent[size];
    }
};
protected Intent(Parcel in) {
    readFromParcel(in);
}
public void readFromParcel(Parcel in) {
    setAction(in.readString8());
    mData = Uri.CREATOR.createFromParcel(in);
    mType = in.readString8();
    mIdentifier = in.readString8();
    mFlags = in.readInt();
    mPackage = in.readString8();
    mComponent = ComponentName.readFromParcel(in);
    ……
    mExtras = in.readBundle();
}

readFromParcel方法中,关键是最后一行,readBundle 用于创建一个新的Bundle对象赋予给mExtras变量。

Parcel.java

public final Bundle readBundle(@Nullable ClassLoader loader) {
    int length = readInt();
    ……
    final Bundle bundle = new Bundle(this, length);
    if (loader != null) {
        bundle.setClassLoader(loader);
    }
    return bundle;
}

Bundle.java

public Bundle(Parcel parcelledData, int length) {
    super(parcelledData, length);
    mFlags = FLAG_ALLOW_FDS;
    maybePrefillHasFds();
}

BaseBundle.java

BaseBundle(Parcel parcelledData, int length) {
    readFromParcelInner(parcelledData, length);
}
private void readFromParcelInner(Parcel parcel, int length) {
    ……
    int offset = parcel.dataPosition();
    parcel.setDataPosition(MathUtils.addOrThrow(offset, length));

    Parcel p = Parcel.obtain();
    p.setDataPosition(0);
    p.appendFrom(parcel, offset, length);
    p.adoptClassCookies(parcel);
    if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
            + ": " + length + " bundle bytes starting at " + offset);
    p.setDataPosition(0);

    mParcelledData = p;
    mParcelledByNative = isNativeBundle;
}

Bundle的构造方法调用了其父类BaseBundle的构造方法,然后是调用了readFromParcelInner方法,readFromParcelInner方法里复制了一个新的Parcel对象然后赋予了mParcelledData变量,它保存的便是序列化后的数据。在反序列化过程时(下面会介绍内在过程)便是通过mParcelledData来还原成原来的数据。

5 Parcelable序列化过程

从上面Intent传递参数过程得知,Intent的writeToParcel方法是序列化的开始,我们来看源码:

Intent.java

public void writeToParcel(Parcel out, int flags) {
    out.writeString8(mAction);
    Uri.writeToParcel(out, mData);
    out.writeString8(mType);
    out.writeString8(mIdentifier);
    out.writeInt(mFlags);
    out.writeString8(mPackage);
    ComponentName.writeToParcel(mComponent, out);
    ……
    out.writeBundle(mExtras);
}

Intent里的writeToParcel方法前面会进行一些Action、Type、Flags、Package等的写入,最后一行会调用到Parcel的writeBundle方法。

Parcel.java

public final void writeBundle(@Nullable Bundle val) {
    ……
    val.writeToParcel(this, 0);
}

Bundle.java

public void writeToParcel(Parcel parcel, int flags) {
??? final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
??? try {
??????? super.writeToParcelInner(parcel, flags);
??? } finally {
??????? parcel.restoreAllowFds(oldAllowFds);
??? }
}

Bundle的父类是BaseBundle,所以再来看下BaseBundle# writeToParcelInner。

BaseBundle.java

void writeToParcelInner(Parcel parcel, int flags) {
    ……
    final ArrayMap<String, Object> map;
    synchronized (this) {
        // unparcel() can race with this method and cause the parcel to recycle
        // at the wrong time. So synchronize access the mParcelledData's content.
        if (mParcelledData != null) {
            if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
                parcel.writeInt(0);
            } else {
                int length = mParcelledData.dataSize();
                parcel.writeInt(length);
                parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
                parcel.appendFrom(mParcelledData, 0, length);
            }
            return;
        }
        map = mMap;
    }
    ……
    int lengthPos = parcel.dataPosition();
    parcel.writeInt(-1); // dummy, will hold length
    parcel.writeInt(BUNDLE_MAGIC);

    int startPos = parcel.dataPosition();
// 关键代码,将数据写入parcel
    parcel.writeArrayMapInternal(map);
    int endPos = parcel.dataPosition();

    // Backpatch length
    parcel.setDataPosition(lengthPos);
    int length = endPos - startPos;
    parcel.writeInt(length);
    parcel.setDataPosition(endPos);
}

上面我们知道,无论是Bundle.putBundle还是Bundle.putParcelable,都是将对象添加到一个ArrayMap<String, Object>类型的mMap中去,这里的parcel.writeArrayMapInternal便是传入这个ArrayMap。

Parcel.java

void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
??? ……

??? final int N = val.size();
??? writeInt(N);

……
??? int startPos;
??? for (int i=0; i<N; i++) {
??????? if (DEBUG_ARRAY_MAP) startPos = dataPosition();
??????? writeString(val.keyAt(i));
??????? writeValue(val.valueAt(i));
??????? ……
??? }
}

WriteArrayMapInternal方法先写入Map的长度,然后遍历Map写入每一项的key和value。着重看写value部分。

public final void writeValue(@Nullable Object v) {
??? if (v == null) {
??????? writeInt(VAL_NULL);
??? } else if (v instanceof String) {
??????? writeInt(VAL_STRING);
??????? writeString((String) v);
??? } else if (v instanceof Integer) {
??????? writeInt(VAL_INTEGER);
??????? writeInt((Integer) v);
??? } else if (v instanceof Map) {
??????? writeInt(VAL_MAP);
??????? writeMap((Map) v);
??? } else if (v instanceof Bundle) {
??????? // Must be before Parcelable
??????? writeInt(VAL_BUNDLE);
??????? writeBundle((Bundle) v);
??? } else if (v instanceof PersistableBundle) {
??????? writeInt(VAL_PERSISTABLEBUNDLE);
??????? writePersistableBundle((PersistableBundle) v);
??? } else if (v instanceof Parcelable) {
??????? // 关键代码,实现了Parcelable的类的写入
??????? writeInt(VAL_PARCELABLE);
????? ??writeParcelable((Parcelable) v, 0);
??? } else if (v instanceof Short) {
??????? writeInt(VAL_SHORT);
??????? writeInt(((Short) v).intValue());
??? } else if (v instanceof XXX) {
?????? ……

} else {
??????? ……
??? }
}

writeValue内根据Object的类型进行不同的写入,当发现对象是Parcelable 后便再调用了writeParcelable方法。

public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
??? ……
??? writeParcelableCreator(p);
??? p.writeToParcel(this, parcelableFlags);
}

Parcelable.java

public interface Parcelable {
??? public void writeToParcel(Parcel dest, @WriteFlags int flags);
}

writeParcelable方法最后一行便是调用了Parcelable的writeToParcel方法,也就是我们手动实现的序列化的地方。

6 Parcelable反序列化过程

从上面Intent传递参数过程得知,在新的Activity里包含着一个新的Intent对象,而Intent内部的Bundle的mParcelledData字段便是保存了前面序列化后的数据。了解反序列化的过程,从Intent.getParcelableExtra或Bundle.getParcelable开始:

Intent.java

public @Nullable <T extends Parcelable> T getParcelableExtra(String name) {
??? return mExtras == null ? null : mExtras.<T>getParcelable(name);
}

Bundle.java

public <T extends Parcelable> T getParcelable(@Nullable String key) {
    unparcel();
    Object o = mMap.get(key);
    if (o == null) {
        return null;
    }
    try {
        return (T) o;
    } catch (ClassCastException e) {
        typeWarning(key, o, "Parcelable", e);
        return null;
    }
}

getParcelable方法中,首先通过unparcel方法将数据还原成新的对象,然后通过key读取Map后返回对象。所以关键就是unparcel方法。

BaseBundle.java

void unparcel() {
    synchronized (this) {
        final Parcel source = mParcelledData;
        if (source != null) {
            initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
        } else {
            if (DEBUG) {
                Log.d(TAG, "unparcel "
                        + Integer.toHexString(System.identityHashCode(this))
                        + ": no parcelled data");
            }
        }
    }
}

因为前面的原因,这次mParcelledData变量已经不为空,所以接下来会调用到initializeFromParcelLocked方法。

private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel, boolean parcelledByNative) {
    ……
    ArrayMap<String, Object> map = mMap;
    if (map == null) {
        map = new ArrayMap<>(count);
    } else {
        map.erase();
        map.ensureCapacity(count);
    }
    try {
        if (parcelledByNative) {
            parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
        } else {
            parcelledData.readArrayMapInternal(map, count, mClassLoader);
        }
    } catch (BadParcelableException e) {
        ……
    } 
……
}

initializeFromParcelLocked方法内,判断parcelledByNative是否为true来决定使用Parcel的readArrayMapSafelyInternal方法还是readArrayMapInternal方法来进行反序列化输出ArrayMap参数。

Parcel.java

void readArrayMapSafelyInternal(@NonNull ArrayMap outVal, int N, @Nullable ClassLoader loader) {
    ……
    while (N > 0) {
        String key = readString();
        if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Read safe #" + (N-1) + ": key=0x"
                + (key != null ? key.hashCode() : 0) + " " + key);
        Object value = readValue(loader);
        outVal.put(key, value);
        N--;
    }
}
void readArrayMapInternal(@NonNull ArrayMap outVal, int N, @Nullable ClassLoader loader) {
    ……
    int startPos;
    while (N > 0) {
        if (DEBUG_ARRAY_MAP) startPos = dataPosition();
        String key = readString();
        Object value = readValue(loader);
        if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Read #" + (N-1) + " "
                + (dataPosition()-startPos) + " bytes: key=0x"
                + Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
        outVal.append(key, value);
        N--;
    }
    outVal.validate();
}

无论是readArrayMapSafelyInternal方法还是readArrayMapInternal方法,都是根据数据长度进行循环调用readValue方法进行读取数据。

public final Object readValue(@Nullable ClassLoader loader) {
    int type = readInt();
    switch (type) {
    case VAL_NULL:
        return null;
    case VAL_STRING:
        return readString();
    case VAL_INTEGER:
        return readInt();
    case VAL_MAP:
        return readHashMap(loader);
    case VAL_PARCELABLE:
// 关键代码,实现了Parcelable的类型的读取
        return readParcelable(loader);
    ……
}

readValue方法对应序列化时的writeValue方法,会根据序列化时标明的数据的类型进行不同的读取,当发现类型是Parcelable后便再调用了readParcelable方法。

public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
// 关键代码1,调到readParcelableCreator方法去获取我们类中的CREATOR
    Parcelable.Creator<?> creator = readParcelableCreator(loader);
    if (creator == null) {
        return null;
    }
    if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
      Parcelable.ClassLoaderCreator<?> classLoaderCreator =
          (Parcelable.ClassLoaderCreator<?>) creator;
      return (T) classLoaderCreator.createFromParcel(this, loader);
    }
// 关键代码2,调用CREATOR里的createFromParcel方法,该方法便是返回带Parcel的构造方法,也就是反序列化的地方
    return (T) creator.createFromParcel(this);
}
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
    ……
    try {
        ClassLoader parcelableClassLoader = (loader == null ? getClass().getClassLoader() : loader);
        Class<?> parcelableClass = Class.forName(name, false, parcelableClassLoader);
        ……
// 关键代码,指定字段名一定是 CREATOR
        Field f = parcelableClass.getField("CREATOR");
        ……
        Class<?> creatorType = f.getType();
        ……
        creator = (Parcelable.Creator<?>) f.get(null);
    } catch (IllegalAccessException e) {
        ……
    } catch (ClassNotFoundException e) {
        ……
    } catch (NoSuchFieldException e) {
        ……
    }
    ……
    return creator;
}

关键代码1中,readParcelableCreator方法通过传入的ClassLoader作为参数,通过反射获取到我们在类中定义的 CREATOR变量,这里也解释了为什么实现Parcelable接口后,除了实现两个必要的接口方法外,还需要声明一定名称必须是CREATOR变量的原因。在获取了CREATOR后,便在关键代码2中,调用CREATOR里的createFromParcel方法,我们在该方法中返回带Parcel的构造方法,也就是我们手动实现反序列化的地方。

7 Parcel原理

从上面的介绍可知,Parcel对象通过obtain方法创建,序列化和反序列化的实际上最后的操作便是我们类中自己实现的writeToParcel方法和带Parcel参数的构造方法中通过一系列writeXXXreadXXX方法来完成。下面来看看Parcel的源码情况。

7.1 Java层的Parcel类

obtain方法创建Parcel对象:

Parcel.java

public static Parcel obtain() {
??? final Parcel[] pool = sOwnedPool;
??? synchronized (pool) {
??????? Parcel p;
??????? for (int i=0; i<POOL_SIZE; i++) {
??????????? p = pool[i];
??????????? if (p != null) {
??????????????? pool[i] = null;
??????????????? if (DEBUG_RECYCLE) {
??????????????????? p.mStack = new RuntimeException();
??????????????? }
??????????????? p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
??????????????? return p;
??????????? }
??????? }
??? }

    // 缓存中没有Parcel对象,则传入0创建一个新的
??? return new Parcel(0);
}

obtain方法先尝试从缓存数组中去获取一个Parcel对象,若缓存中没有则传入0创建一个新的对象。

private long mNativePtr; // 用于存放 Native 层的 Parcel 对象的指针地址

private Parcel(long nativePtr) {
??? if (DEBUG_RECYCLE) {
??????? mStack = new RuntimeException();
??? }
??? init(nativePtr);
}
private void init(long nativePtr) {
??? if (nativePtr != 0) {
??????? mNativePtr = nativePtr;
??????? mOwnsNativeParcelObject = false;
??? } else {
??????? mNativePtr = nativeCreate();
??????? mOwnsNativeParcelObject = true;
??? }
}

private static native long nativeCreate();

Parcel的构造函数调用了init方法进行初始化Parcel,如果传入0,则通过nativeCreate方法创建,并返回一个指针地址数值赋值给mNativePtr变量。其中,nativeCreate方法是一个Native方法。而我们进行序列化和反序列化的一系列writeXXX和readXXX方法其实也是有对应的Native方法,Parcel类只是一个代理的空壳类:

public final void writeInt(int val) {
??? nativeWriteInt(mNativePtr, val);
}
public final void writeLong(long val) {
??? nativeWriteLong(mNativePtr, val);
}
public final void writeFloat(float val) {
??? nativeWriteFloat(mNativePtr, val);
}
public final void writeDouble(double val) {
??? nativeWriteDouble(mNativePtr, val);
}
public final void writeString(@Nullable String val) {
??? mReadWriteHelper.writeString(this, val);
}
public final int readInt() {
??? return nativeReadInt(mNativePtr);
}
public final long readLong() {
??? return nativeReadLong(mNativePtr);
}
public final float readFloat() {
??? return nativeReadFloat(mNativePtr);
}
public final double readDouble() {
??? return nativeReadDouble(mNativePtr);
}
public final String readString() {
??? return mReadWriteHelper.readString(this);
}
public static class ReadWriteHelper {
??? public static final ReadWriteHelper DEFAULT = new ReadWriteHelper();
??? public void writeString(Parcel p, String s) {
??????? nativeWriteString(p.mNativePtr, s);
??? }
??? public String readString(Parcel p) {
??????? return nativeReadString(p.mNativePtr);
??? }
}
private static native void nativeWriteInt(long nativePtr, int val);
private static native void nativeWriteLong(long nativePtr, long val);
private static native void nativeWriteFloat(long nativePtr, float val);
private static native void nativeWriteDouble(long nativePtr, double val);
static native void nativeWriteString(long nativePtr, String val);
private static native int nativeReadInt(long nativePtr);
private static native long nativeReadLong(long nativePtr);
private static native float nativeReadFloat(long nativePtr);
private static native double nativeReadDouble(long nativePtr);
static native String nativeReadString(long nativePtr);

7.2 C++层的android_os_Parcel类

正常情况下,Android Studio开发时所下载的SDK源码里并不会包含Native层的代码,所以我们如果需要查阅C++层的代码需要自己另外进行下载,或者通过在线查阅,如:AndroidXRef 。回来正题,Parcel.java中所调用的Native代码的实现在android_os_Parcel.cpp中,继续查看关键方法的实现。

\frameworks\base\core\jni\android_os_Parcel.cpp

static const JNINativeMethod gParcelMethods[] = {
        ……
        {"nativeCreate",              "()J", (void*)android_os_Parcel_create},
        ……
        {"nativeWriteInt",            "(JI)V", (void*)android_os_Parcel_writeInt},
        {"nativeWriteLong",           "(JJ)V", (void*)android_os_Parcel_writeLong},
        {"nativeWriteFloat",          "(JF)V", (void*)android_os_Parcel_writeFloat},
        {"nativeWriteDouble",         "(JD)V", (void*)android_os_Parcel_writeDouble},
        {"nativeWriteString",         "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
        ……
        {"nativeReadInt",             "(J)I", (void*)android_os_Parcel_readInt},
        {"nativeReadLong",            "(J)J", (void*)android_os_Parcel_readLong},
        {"nativeReadFloat",           "(J)F", (void*)android_os_Parcel_readFloat},
        {"nativeReadDouble",          "(J)D", (void*)android_os_Parcel_readDouble},
        {"nativeReadString",          "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString},
        ……
};
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) {
    // 创建 native 层的 Parcel 对象
    Parcel* parcel = new Parcel();
    // 返回其指针的long值
    return reinterpret_cast<jlong>(parcel);
}
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val)
{
    // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        // 使用Parcel指针调用其 writeInt32() 方法写入数据
        const status_t err = parcel->writeInt32(val);
    if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}
static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val)
{
    // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        status_t err = NO_MEMORY;
        if (val) {
            // 获取需要写入的字符串
            const jchar* str = env->GetStringCritical(val, 0);
            if (str) {
                // 使用Parcel指针调用其 writeString16() 方法写入数据
                err = parcel->writeString16(
                    reinterpret_cast<const char16_t*>(str),
                    env->GetStringLength(val));
                // 释放内存
                env->ReleaseStringCritical(val, str);
            }
        } else {
            err = parcel->writeString16(NULL, 0);
        }
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}
static jint android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        // 使用Parcel指针调用其 readInt32() 方法读取数据
        return parcel->readInt32();
    }
    return 0;
}
static jstring android_os_Parcel_readString(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    // nativePtr 是 android_os_Parcel_create 返回的 Parcel 对象的指针值,这里强转回 Parcel 指针
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        size_t len;
        // 使用Parcel指针调用其 readString16Inplace() 方法读取数据
        const char16_t* str = parcel->readString16Inplace(&len);
        if (str) {
            return env->NewString(reinterpret_cast<const jchar*>(str), len);
        }
        return NULL;
    }
    return NULL;
}

由于代码量很多,这里只列出创建native层的Parcel对象和关于Int和String两种类型的写入和读取代码,所有的类型写入和读取方法都是首先通过传入的nativePtr变量强转成Parcel指针,此变量便是android_os_Parcel_create方法创建Parcel对象返回的指针值,接着使用Parcel指针来调用相应的写入和读取操作。

frameworks\native\libs\binder\Parcel.cpp

status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}
template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
    // 判断内存是否足够
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
    // 将指针移动到偏移的位置(首地址+指针的偏移量),接着写入val到内存去
    *reinterpret_cast<T*>(mData+mDataPos) = val;
    // 更新内存的偏移量
    return finishWrite(sizeof(val));
    }

    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
        return err;
}
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
    if (str == NULL) return writeInt32(-1);
        // 写入字符串值长度
        status_t err = writeInt32(len);
    if (err == NO_ERROR) {
        // 计算字符串所需的内存
        len *= sizeof(char16_t);
        // 开辟缓存字符串的内存,更新内存地址的偏移量
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
        if (data) {
            // 将字符串复制到内存中缓存
            memcpy(data, str, len);
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}
int32_t Parcel::readInt32() const
{
    return readAligned<int32_t>();
}
template<class T>
status_t Parcel::readAligned(T *pArg) const {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
    // 判断内存是否足够
    if ((mDataPos+sizeof(T)) <= mDataSize) {
        // 将偏移指针赋给任意类型的数据指针
        const void* data = mData+mDataPos;
        // 更新指针的偏移量
        mDataPos += sizeof(T);
        // 返回数据
        *pArg =  *reinterpret_cast<const T*>(data);
        return NO_ERROR;
    } else {
        return NOT_ENOUGH_DATA;
    }
}
String16 Parcel::readString16() const
{
    size_t len;
    // 读取字符串
    const char16_t* str = readString16Inplace(&len);
    if (str) return String16(str, len);
    return String16();
}
const char16_t* Parcel::readString16Inplace(size_t* outLen) const
{
    int32_t size = readInt32();
    if (size >= 0 && size < INT32_MAX) {
        *outLen = size;
        // 读取字符串
        const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
        if (str != NULL) {
            return str;
        }
    }
    *outLen = 0;
    return NULL;
}

所以序列化和反序列化最终的写入和读取是在native层的Parcel.cpp中完成,在Parcel.cpp里会进行开辟一块连续的内存用来进行数据的缓存,其中内存的首地址是mData,偏移量是mDataPos。

当写入一个数据时,首先会将指针移动到对应的位置,即mData + mDataPos,然后再将数据写入内存,最后会重新计算mDataPos 的偏移量。到下次要写入数据时同理会将数据写入到内存的后面。

也是因为开辟的是一块连续的内存,所以在读取数据时只要保证跟写入数据的顺序一致,便能将内存中的数据读出。

8 总结

  1. 从使用上对比 Serializable 和 Parcelable,Serializable 会显得简单一些,两者都是实现不同的接口,但是 Serializable 可全自动序列化和反序列化,Parcelable 需要手动去 write 和 read 一系列方法才能完成序列化和反序列化。
  2. 从场景来考虑,Serializable 支持数据持久化存储到本地磁盘、网络传输等,Parcelable 则不支持。如果只在组件间或者跨进程组件间的传输数据的场景,虽然两者都是支持的。但是在内存使用和性能上 Parcelable 会优出 Serializable 很多,因为 Serializable 在序列化和反序列化过程中都需要较多的反射和IO操作,Parcelable 设计的目的就是为了提高这种场景下数据传输性能,因为其内部是通过Parcel来实现,Parcel?是一种可通过?IBinder?高性能传输数据和对象的容器,内部采用共享内存的方式实现用户空间和内核空间的数据交换,本质是?Native?层的共享内存。
  3. Parcelable的使用需要类继承Parcelable接口,并且必须重写describeContents 和 writeToParcel 两个方法和存在一个带Parcel类型参数的构造方法,以及必须声明一个非空的、名字必须是CREATOR的、类型是Parcelable.Creator<T>的静态变量。
  4. writeToParcel方法是实现序列化写操作的地方,而带Parcel参数的构造方法是实现反序列化读操作的地方。Parcel对象的写入和读取的顺序必须保持一致,否则会发生数值错误或者类型异常崩溃情况。
  5. 从IPC过程可知,当Client端发起跨进程请求时前,先调用Intent的writeToParcel方法,将Intent内存中的ArrayMap输出一个android.os.Parcel 类型的_data变量进行Parcelable的序列化。
  6. 从IPC过程可知,远程请求会通过系统底层封装后交由onTransact方法来处理,些时会接收android.os.Parcel 类型的_data变量,再创建新的Intent对象,Intent的构造函数会接收这个_data变量,并使用mParcelledData变量进行存储起来,当调用到Intent的getParcelableExtra或者getParcelable方法获取数据时,便是对内存中mParcelledData进行反序列化成ArrayMap。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-12 17:41:04  更:2022-03-12 17:42:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 17:21:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码