preface

In Android, when serialization is common, Google officially recommends using Parcelable because it is much more efficient (about 10 times) than Serializable provided by the JDK.

Here we first discuss how to use Parcelable, and then from the source of the interpretation of Parcelable efficiency why so high. Finally, analyze the application scenarios of Parcelable and the differences between Parcelable and Serializable.

How to use

Define a POJO object that implements Parcelable as follows,

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

public class Book implements Parcelable {
    private int id;
    private String name;

    //setter & getter & constructor
    / /...

    // Here is how to implement the Parcelable interface
    // Except for special file descriptor scenarios to serialize, return zero
    @Override
    public int describeContents(a) {
        return 0;
    }

    / / the serialization
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            // A custom private constructor to deserialize the corresponding member variable value
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return newBook[size]; }};// Generate an object with the same contents as the previous object based on the deserialized properties
    private Book(Parcel in) {
        // Remember to deserialize attributes in the same order as before!!id = in.readInt(); name = in.readString(); }}Copy the code

The following code shows how to serialize data from the Book object between activities,

// Pass Book Book = new Book(123, "Hello world"); Intent intent = new Intent(FirstActivity.this, SecondActivity.class); intent.putExtra("book_data", book); startActivity(intent); / / accept Book Book. = (Book) getIntent getParcelableExtra (" book_data);Copy the code

Analysis of the source code

Looking at the source code, Parcelable is just an interface and the implementation is handed to a Parcel object. For example, when we call the methods in the Parcel class at writeToParcel, we can see that the implementation is handed over to Native.

.public final void writeInt(int val) { nativeWriteInt(mNativePtr, val); }...@FastNative
private static native void nativeWriteInt(long nativePtr, int val);
@FastNative
private static native void nativeWriteLong(long nativePtr, long val);
@FastNative
private static native void nativeWriteFloat(long nativePtr, float val); .Copy the code

Since it is a native implementation, you need to look at the Android source code. We open androidXref.com (fQ may be needed, aoSPxref.com is recommendable) and search for nativeWriteInt in Android source code. Orientation to the corresponding c + + implementation frameworks in directory/base/core/jni android_os_Parcel. CPP, interested friends can look at.

In Android source code, many native methods are dynamically registered. Here, we will not repeat how to find the corresponding implementation of C, but let’s go straight down.

static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
    // Force a Parcel object by pointer reinterpretation
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if(parcel ! =NULL) {
        // The writeInt32 function in the Parcel is called after all
        const status_t err = parcel->writeInt32(val);
        if(err ! = NO_ERROR) { signalExceptionForError(env, clazz, err); }}}...// A generic template method is actually called
status_t Parcel::writeInt32(int32_t val)
{
    returnwriteAligned(val); }...// Template method
/ / which
//mData indicates the first address pointing to the Parcel memory
//mDataPos indicates the first address pointing to the Parcel's free memory
//mDataCapacity specifies the size of memory allocated by a Parcel
template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
    // Determine if adding val will exceed the available size
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        //reinterpret_cast is a c++ reinterpret_cast operation
        // mData + mDataPos gets the physical address, which is converted to a pointer to type T (the actual type of the input parameter)
        // Then assign val to what the pointer points to
        *reinterpret_cast<T*>(mData+mDataPos) = val;
        // The main logic is to change the offset address of mDataPos
        // Add the offset address to the number of bytes of newly added data
        return finishWrite(sizeof(val));
    }
    // If the available size is exceeded, execute the growth function
    // Then goto the restart_write tag above to perform the write logic
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}
Copy the code

Using the code above, writeInt32 writes data to a shared memory, so we use a Parcel object to read values from readInt. Do the same analysis, as follows,

template<class T>
status_t Parcel::readAligned(T *pArg) const {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
    if ((mDataPos+sizeof(T)) <= mDataSize) {
        // Get the address to read the data
        const void* data = mData+mDataPos;
        //mDataPos points to the next data
        mDataPos += sizeof(T);
        // Fetch data according to the data pointer type
        *pArg =  *reinterpret_cast<const T*>(data);
        return NO_ERROR;
    } else {
        returnNOT_ENOUGH_DATA; }}Copy the code

Write data to a Parcel memory address 12,34; The data is read from the start address (the Android system resets mDataPos to the start value when reading)+ the number of bytes corresponding to the pointer type, first 12, then 34.

This is why it is important to keep the corresponding member variables in the same read/write order when writing serialization methods.

Differences from Serializable

  1. ParcelableIt is very quick to serialize in memory, becauseSerializableFrequent I/ OS are required.
  2. ParcelableThe implementation is complex and the consistency of the read and write order should be paid attention to.SerializableThe implementation is relatively simple.
  3. ParcelableNot suitable for data persistence, but Serializable.