深入c++層分析Serializable和Parcelable的區別

深入c++層分析Serializable和Parcelable的區別

一、Serializable的源碼分析

Serializable是爲了保存對象的屬性到本地文件、網絡傳輸等方便數據傳輸,由於對應使用了ObjectInputStream和ObjectOutputStream進行讀寫,所以在讀取過程中會使用了反射以及產生過多的臨時變量。

由於Serializable的性能過低,不太適用於性能資源相對緊張的嵌入式設備應用間傳遞數據,所以Android設計了新序列化方式–Parcelable用於內存間傳遞數據

接下來我們從ObjectOutputStream讀取數據來分析爲什麼消耗過多資源

1、首先來看一下ObjectOutputStream產生的中間變量

public class ObjectOutputStream
    extends OutputStream implements ObjectOutput, ObjectStreamConstants
{

    private static class Caches {
        /** 子類安全性審覈結果的緩存 */
        static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
            new ConcurrentHashMap<>();

        /** 將WeakReferences引用到審覈的子類中 */
        static final ReferenceQueue<Class<?>> subclassAuditsQueue =
            new ReferenceQueue<>();
    }

    /** 用於處理塊數據轉換的過濾流,是ObjectOutputStream的內部類,實際將數據寫入的類 */
    private final BlockDataOutputStream bout;
    /** 後面會構造一個大小爲10,負載因子爲3的HandleTable和ReplaceTable */
    private final HandleTable handles;
    private final ReplaceTable subs;

    /** 流的協議版本 */
    private int protocol = PROTOCOL_VERSION_2;
    /** 遞歸深度 */
    private int depth;

    /** 用於寫入基本類型字段值的緩衝區 */
    private byte[] primVals;

    /** 如果true,子類重寫writeObjectOverride()處理,否則writeObject()處理   */
    private final boolean enableOverride;
    /** 如果true,調用replaceObject() */
    private boolean enableReplace;

    // 下面的值只在上行調用writeObject()/writeExternal()時有效
    /**
     * 上行調用類定義的writeObject方法時的上下文,持有當前被序列化的對象和當前對象描述符。在非 writeObject上行調用時爲null
     */
    private SerialCallbackContext curContext;
    /** 當前PutField對象 */
    private PutFieldImpl curPut;

    /** 常規存儲用於debug棧信息 */
    private final DebugTraceInfoStack debugInfoStack;

    …………
}

上面只是一些成員變量,還不包含局部變量就已經產生了變量來緩存數據,如果序列化對象的層級和深度加深,好用的資源可想而知,下面將會挑選重點來闡述序列化的過程

2、序列化過程

(1)有多個構造函數,這裏選取一個public來分析

public ObjectOutputStream(OutputStream out) throws IOException {
        //驗證子類是否違反安全約束
        verifySubclass();
        //構造一個BlockDataOutputStream用於向out寫入序列化數據,實際寫入的類對象
        bout = new BlockDataOutputStream(out);
        //構造一個大小爲10,負載因子爲3的HandleTable
        handles = new HandleTable(10, (float) 3.00);
        //構造一個大小爲10,負載因子爲3的ReplaceTable
        subs = new ReplaceTable(10, (float) 3.00);
        //恆爲false,除非子類調用protected構造方法
        enableOverride = false;
        //向流的頭裏寫入魔數和版本號
        writeStreamHeader();
        //將緩存模式打開,寫入數據時先寫入緩衝區
        bout.setBlockDataMode(true);
        //是否開啓debug信息棧
        if (extendedDebugInfo) {
            debugInfoStack = new DebugTraceInfoStack();
        } else {
            debugInfoStack = null;
        }
    }

(2)verifySubclass方法

private void verifySubclass() {
        Class<?> cl = getClass();
        //如果是當前類,直接返回
        if (cl == ObjectOutputStream.class) {
            return;
        }
        //獲取安全管理器是否有對應的權限,沒有則返回
        SecurityManager sm = System.getSecurityManager();
        if (sm == null) {
            return;
        }
        //從弱引用隊列中出隊所有類,並移除緩存中相同的類
        processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
        //將ObjectOutputStream的子類存入Caches類的對象中
        WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
        Boolean result = Caches.subclassAudits.get(key);
        if (result == null) {
            //檢查這個子類是否安全
            result = Boolean.valueOf(auditSubclass(cl));
            //將結果存儲到緩存
            Caches.subclassAudits.putIfAbsent(key, result);
        }
        if (result.booleanValue()) {
            return;
        }
        //驗證是否有實現權限,  如果沒有權限則拋出SecurityException異常
        sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
    }

(3)再來看writeStreamHeader方法,寫入了哪些頭信息

protected void writeStreamHeader() throws IOException {
    bout.writeShort(STREAM_MAGIC); //寫入兩個字節:0xAC和0xED
    bout.writeShort(STREAM_VERSION); //寫入兩個字節:0x00和0x05
}

(4)通常我們構造了ObjectOutputStream oos = new ObjectOutputStream(out)後,會調用writeObject(Object)來寫入對象


public final void writeObject(Object obj) throws IOException {
        //是否子類重寫了,如果重寫,交由子類的writeObjectOverride處理
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            //寫入對象
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                //安卓實現中使用try-catch忽略了次要的一些異常
                try {
                    writeFatalException(ex);
                } catch (IOException ex2) {
                    
                }
            }
            throw ex;
        }
    }


/**
  * 基礎writeObject / writeUnshared實現。
  **/
private void writeObject0(Object obj, boolean unshared)
        throws IOException
    {
        //關閉緩存模式
        boolean oldMode = bout.setBlockDataMode(false);
        depth++;
        try {
            // 處理以前寫過的和不可替換的對象
            int h;
            if ((obj = subs.lookup(obj)) == null) {
                writeNull();
                return;
            } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                writeHandle(h);
                return;
            // 安卓改變 start: 使Class和ObjectStreamClass可替換
            /*
            } else if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
                return;
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
                return;
            */
            // 安卓改變 end: 使Class和ObjectStreamClass可替換
            }

            //檢查可替換的對象
            Object orig = obj;
            Class<?> cl = obj.getClass();
            //序列化對象對應的Class對象的詳細信息
            ObjectStreamClass desc;

            // BEGIN 安卓改變: 僅調用一次writeReplace,在java中for循環中判斷始終爲true,不需要循環
            /*
            for (;;) {
                // REMIND: 對於strings/arrays跳過檢查?
                Class<?> repCl;
                desc = ObjectStreamClass.lookup(cl, true);
                if (!desc.hasWriteReplaceMethod() ||
                    (obj = desc.invokeWriteReplace(obj)) == null ||
                    (repCl = obj.getClass()) == cl)
                {
                    break;
                }
                cl = repCl;
                desc = ObjectStreamClass.lookup(cl, true);
                break;
            }
            */
            // 只替換一次通過

            Class repCl;
            //獲取序列化對象對應的Class對象詳細信息
            desc = ObjectStreamClass.lookup(cl, true);
            if (desc.hasWriteReplaceMethod() &&
                (obj = desc.invokeWriteReplace(obj)) != null &&
                (repCl = obj.getClass()) != cl)
            {
                cl = repCl;
                desc = ObjectStreamClass.lookup(cl, true);
            }
            // END 安卓改變: 僅調用一次writeReplace,在java中for循環中判斷始終爲true,不需要循環

            if (enableReplace) {
                //replaceObject用來替換這個對象進行序列化,默認實現爲空,一般用於子類重寫實現序列化的定製
                Object rep = replaceObject(obj);
                //如果對象被替換了
                if (rep != obj && rep != null) {
                    cl = rep.getClass();
                    //重新查找對應的ObjectStreamClass
                    desc = ObjectStreamClass.lookup(cl, true);
                }
                obj = rep;
            }

            // 如果對象被替換,則再次運行原始檢查
            if (obj != orig) {
                subs.assign(orig, obj);
                if (obj == null) {
                    writeNull();
                    return;
                } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                    writeHandle(h);
                    return;
                // BEGIN 安卓改變:使Class和ObjectStreamClass可替換
                /*
                } else if (obj instanceof Class) {
                    writeClass((Class) obj, unshared);
                    return;
                } else if (obj instanceof ObjectStreamClass) {
                    writeClassDesc((ObjectStreamClass) obj, unshared);
                    return;
                */
                // END 安卓改變:使Class和ObjectStreamClass可替換
                }
            }

            // remaining cases
            // BEGIN 安卓改變: 使Class和ObjectStreamClass可替換
            if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
            // END 安卓改變: 使Class和ObjectStreamClass可替換.
            //序列化對象類型爲String、數組、枚舉時,調用定製的寫入方法
            } else if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            //一般對象類型的寫入,這裏就是需要實現Serializable的對象寫入的地方
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                //如果沒有實現序列化接口會拋出異常
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
        } finally {
            //結束方法前將方法棧深減去1
            depth--;
            //切換回之前的緩存模式
            bout.setBlockDataMode(oldMode);
        }
    }

(5)writeOrdinaryObject

/**
  * 最終會以一種遞歸的形式寫入對象信息。
  **/
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc,
         boolean unshared) throws IOException {
    if (extendedDebugInfo)
        debugInfoStack.push(
                (depth == 1 ? "root " : "") + "object (class \"" +
                obj.getClass().getName() + "\", " + obj.toString() + ")");
    try {
        //檢查ObjectStreamClass對象
        desc.checkSerialize();
        //寫入字節0x73
        bout.writeByte(TC_OBJECT);
        //寫入對應的Class對象的信息
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);//如果是share模式把這個對象加入緩存
        if (desc.isExternalizable() && !desc.isProxy()) {
            writeExternalData((Externalizable) obj);
        } else {
            //寫入這個對象變量信息及其父類的成員變量
            //將這個實例及其父類基本數據類型寫入文件,如果檢測到有引用類型,那麼會繼續調用
            writeSerialData(obj, desc);
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}

(6)寫入描述信息

    /**
     * 將給定類描述符的表示形式寫入流。
     */
    private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
        throws IOException
    {
        int handle;
        if (desc == null) {
            //描述爲空,寫null
            writeNull();
        } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
            //共享模式且緩存中已有該類描述符時,寫對應句柄值
            writeHandle(handle);
        } else if (desc.isProxy()) {
            //描述信息爲動態代理類寫入
            writeProxyDesc(desc, unshared);
        } else {
            //標準類寫入
            writeNonProxyDesc(desc, unshared);
        }
    }

    private boolean isCustomSubclass() {
        // 如果此類是ObjectOutputStream的自定義子類,則返回true
        return getClass().getClassLoader()
                   != ObjectOutputStream.class.getClassLoader();
    }

    /**
     * 將表示動態代理類的類描述符寫入流中。
     */
    private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
        throws IOException
    {
        bout.writeByte(TC_PROXYCLASSDESC);
        //共享則寫入緩存
        handles.assign(unshared ? null : desc);
        //獲取類實現的接口,然後寫入接口個數和接口名。此處使用反射
        Class<?> cl = desc.forClass();
        Class<?>[] ifaces = cl.getInterfaces();
        bout.writeInt(ifaces.length);
        //寫入接口名
        for (int i = 0; i < ifaces.length; i++) {
            bout.writeUTF(ifaces[i].getName());
        }
        //打開緩存模式
        bout.setBlockDataMode(true);
        //如果是子類,校驗包權限。此處使用反射
        if (cl != null && isCustomSubclass()) {
            ReflectUtil.checkPackageAccess(cl);
        }
        //裝配動態代理類,子類可以重寫這個方法存儲類信息到流中,默認什麼也不做
        annotateProxyClass(cl);
        //關閉緩存模式
        bout.setBlockDataMode(false);
        //寫入塊結束符
        bout.writeByte(TC_ENDBLOCKDATA);
        //接下來再遞歸寫入父類信息
        writeClassDesc(desc.getSuperDesc(), false);
    }

    /**
     * 將代表標準(即不是動態代理)類的類描述符寫入流中。
     */
    private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
        throws IOException
    {
        bout.writeByte(TC_CLASSDESC);
        handles.assign(unshared ? null : desc);

        if (protocol == PROTOCOL_VERSION_1) {
            // 不要使用舊協議調用類描述符寫鉤子
            desc.writeNonProxy(this);
        } else {
            writeClassDescriptor(desc);
        }

        Class<?> cl = desc.forClass();
        bout.setBlockDataMode(true);
        if (cl != null && isCustomSubclass()) {
            ReflectUtil.checkPackageAccess(cl);
        }
        //子類可以重寫這個方法存儲類信息到流中,默認什麼也不做
        annotateClass(cl);
        bout.setBlockDataMode(false);
        bout.writeByte(TC_ENDBLOCKDATA);
        //接下來再遞歸寫入父類信息
        writeClassDesc(desc.getSuperDesc(), false);
    }

(8)寫入字段信息

    /**
     * 通過調用其writeExternal()方法來寫入給定對象的可外部化數據。
     */
    private void writeExternalData(Externalizable obj) throws IOException {
        PutFieldImpl oldPut = curPut;
        curPut = null;

        if (extendedDebugInfo) {
            debugInfoStack.push("writeExternal data");
        }
        //存儲上下文
        SerialCallbackContext oldContext = curContext;
        try {
            curContext = null;
            if (protocol == PROTOCOL_VERSION_1) {
                //協議版本1
                obj.writeExternal(this);
            } else {
                //默認協議是2,所以會使用塊輸出流
                bout.setBlockDataMode(true);
                //取決於類的實現
                obj.writeExternal(this);
                bout.setBlockDataMode(false);
                //寫入塊數據結束符
                bout.writeByte(TC_ENDBLOCKDATA);
            }
        } finally {
            //將上下文改變回來
            curContext = oldContext;
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }

        curPut = oldPut;
    }

    /**
     * 爲給定對象的每個可序列化類(從超類到子類)寫入實例數據。
     */
    private void writeSerialData(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
        for (int i = 0; i < slots.length; i++) {
            ObjectStreamClass slotDesc = slots[i].desc;
            //重寫了writeObject方法
            if (slotDesc.hasWriteObjectMethod()) {
                PutFieldImpl oldPut = curPut;
                curPut = null;
                SerialCallbackContext oldContext = curContext;

                if (extendedDebugInfo) {
                    debugInfoStack.push(
                        "custom writeObject data (class \"" +
                        slotDesc.getName() + "\")");
                }
                try {
                    curContext = new SerialCallbackContext(obj, slotDesc);
                    bout.setBlockDataMode(true);
                    slotDesc.invokeWriteObject(obj, this);
                    bout.setBlockDataMode(false);
                    bout.writeByte(TC_ENDBLOCKDATA);
                } finally {
                    curContext.setUsed();
                    curContext = oldContext;
                    if (extendedDebugInfo) {
                        debugInfoStack.pop();
                    }
                }

                curPut = oldPut;
            } else {
                //如果沒有重寫writeObject則輸出默認內容
                defaultWriteFields(obj, slotDesc);
            }
        }
    }

    /**
     * 獲取並寫入給定對象的可序列化字段的值以流式傳輸。 給定的類描述符指定要寫入的字段值,以及應按什麼順序寫入它們。
     */
    private void defaultWriteFields(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        Class<?> cl = desc.forClass();
        if (cl != null && obj != null && !cl.isInstance(obj)) {
            throw new ClassCastException();
        }

        desc.checkDefaultSerialize();

        int primDataSize = desc.getPrimDataSize();
        if (primVals == null || primVals.length < primDataSize) {
            primVals = new byte[primDataSize];
        }
        //將基本類型數據的字段值存入緩衝區
        desc.getPrimFieldValues(obj, primVals);
        //輸出緩衝區內容
        bout.write(primVals, 0, primDataSize, false);

        ObjectStreamField[] fields = desc.getFields(false);
        //獲取非基本數據類型對象
        Object[] objVals = new Object[desc.getNumObjFields()];
        int numPrimFields = fields.length - objVals.length;
        desc.getObjFieldValues(obj, objVals);
        for (int i = 0; i < objVals.length; i++) {
            if (extendedDebugInfo) {
                debugInfoStack.push(
                    "field (class \"" + desc.getName() + "\", name: \"" +
                    fields[numPrimFields + i].getName() + "\", type: \"" +
                    fields[numPrimFields + i].getType() + "\")");
            }
            try {
                //遞歸繼續寫入
                writeObject0(objVals[i],
                             fields[numPrimFields + i].isUnshared());
            } finally {
                if (extendedDebugInfo) {
                    debugInfoStack.pop();
                }
            }
        }
    }

(9)舉幾個基礎類型寫入

    private void writeString(String str, boolean unshared) throws IOException {
        handles.assign(unshared ? null : str);
        //獲得UTF編碼長度
        long utflen = bout.getUTFLength(str);
        if (utflen <= 0xFFFF) {
            //寫入類型標誌符
            bout.writeByte(TC_STRING);
            //寫入utf編碼數據
            bout.writeUTF(str, utflen);
        } else {
            //寫入類型標誌符
            bout.writeByte(TC_LONGSTRING);
            //寫入utf編碼數據
            bout.writeLongUTF(str, utflen);
        }
    }

    private void writeEnum(Enum<?> en,
                           ObjectStreamClass desc,
                           boolean unshared)
        throws IOException
    {   
        //寫入類型標誌符
        bout.writeByte(TC_ENUM);
        ObjectStreamClass sdesc = desc.getSuperDesc();
        //寫入類信息
        writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
        //共享模式寫入緩存
        handles.assign(unshared ? null : en);
        //以字符串形式寫入name
        writeString(en.name(), false);
    }

(10)BlockDataOutputStream類,在這裏我就不分析了,喜歡深入瞭解的朋友,可以繼續查看BlockDataOutputStream的相關write*方法



一.Parcelable的源碼分析

Parcelable在性能消耗和內存開銷上都比Serializable要小,所以內存間傳遞數據推薦使用Parcelable。Parcelable不適用於磁盤上存儲數據,由於Parcelable存儲數據是有序的,在磁盤上持久化數據,會隨着對象的不同版本,新增、刪除、交換位置等原因,導致讀取數據異常。

1、編碼實現形式

(1)編寫一個實體類


public class Bean1 implements Parcelable {
    private String name;
    private int age;

    protected Bean1(Parcel in) {
        //從Parcel對象讀取反序列化值,必須和寫入保持一致
        name = in.readString();
        age = in.readInt();
    }


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

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

    @Override
    public int describeContents() {
        return 0;//默認爲0,如果需要序列化file description,改爲1
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //向Parcel對象寫入數據用於本地化--必須和讀取一致
        dest.writeString(name);
        dest.writeInt(age);
    }
}

2.從編寫aidl看Parcelable序列化過程

(1)編寫aidl

// IBean.aidl
package com.yetao.android.library.sdk28;

// Declare any non-default types here with import statements

interface IBean {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

package com.yetao.android.library.sdk28;
// 在這裏使用import語句聲明所有非默認類型

//android studio自動生成
public interface IBean extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.yetao.android.library.sdk28.IBean {
        private static final java.lang.String DESCRIPTOR = "com.yetao.android.library.sdk28.IBean";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.yetao.android.library.sdk28.IBean interface,
         * generating a proxy if needed.
         */
        public static com.yetao.android.library.sdk28.IBean asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.yetao.android.library.sdk28.IBean))) {
                return ((com.yetao.android.library.sdk28.IBean) iin);
            }
            return new com.yetao.android.library.sdk28.IBean.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @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;

            //按順序從data中讀出字段值,必須按順序寫
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.yetao.android.library.sdk28.IBean {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * 演示一些可用作參數的基本類型並以AIDL返回值。
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    //一下是序列化數據的寫入,沒有寫入字段名,所以在讀取的時候必須按順序
                    //所以不能用於磁盤持久化中,一旦文件字段增刪查改,反序列化將出現問題
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * 演示一些可用作參數的基本類型並以AIDL返回值。
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

(2)Parcel創建

上面介紹了aidl通過Parcel寫入數據來做交互,其實Parcel在java和c++層都對應有具體的操作,java層提供接口,實現在c++層。

先來看java層的Parcel初始化

Parcel.java

    /**
    * @param nativePtr 是c++層Parcel對應的地址
    **/
   private Parcel(long nativePtr) {
        if (DEBUG_RECYCLE) {
            mStack = new RuntimeException();
        }
        //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
        //調用初始化
        init(nativePtr);
    }  


    private void init(long nativePtr) {
        //如果傳入了地址,則直接指向該地址
        if (nativePtr != 0) {
            mNativePtr = nativePtr;
            mOwnsNativeParcelObject = false;
        } else {
            //如果沒有傳入,則調用本地方法創建
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }

    private static native long nativeCreate();

我們來深入c++層分析是怎麼創建的

//  /frameworks/base/core/jni/android_os_Parcel.cpp
//nativeCreate映射到android_os_Parcel_create方法
{"nativeCreate",              "()J", (void*)android_os_Parcel_create},

static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
    //創建出c++層Parcel對象
    Parcel* parcel = new Parcel();
    //將指針地址轉換成long型地址返回java層,被mNativePtr持有
    return reinterpret_cast<jlong>(parcel);
}


(2)Parcel的寫入

說道寫入我們先來c++層看看Pracel的幾個變量

/frameworks/base/core/jni/android_os_Parcel.cpp

uint8_t*            mData;//數據存儲的指針
size_t              mDataSize;//數據的大小
size_t              mDataCapacity;//數據的容量
mutable size_t      mDataPos;//數據存儲的指針位置

後面圍繞着這幾個變量,來對數據進行寫入操作,以writeInt()爲例,還是從java到c++層分析

Parcel.java

    /** 
     * 在當前dataPosition()處將整數值寫入包裹,
     * 如果需要,可增加dataCapacity()。
     */
    public final void writeInt(int val) {
        nativeWriteInt(mNativePtr, val);
    }
    //傳入c++Parcel對象指針和要寫入的值
    private static native void nativeWriteInt(long nativePtr, int val);

上面java層沒有實現具體操作,具體操作還是在c++層實現

/frameworks/base/core/jni/android_os_Parcel.cpp
//將nativeWriteInt方法映射到android_os_Parcel_writeInt
{"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},

static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
    //從long型指針強轉成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);
        }
    }
}

jni層調用Parcel的writeInt32(val)來寫入值

/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:
        *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;
}

writeAligned主要做了以下幾步:
1.判斷寫入容量是否足夠
2.不夠的話,先擴容然後再寫入;如果容量夠,直接寫入
3.寫入數據完成後,寫入數據長度。

寫入String類型數據的有點不一樣,來看看如何寫入,直接看c++層

/frameworks/base/core/jni/android_os_Parcel.cpp
//映射方法
{"nativeWriteString",  "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},

static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        status_t err = NO_MEMORY;
        if (val) {
            //用掉writeString16寫入str
            const jchar* str = env->GetStringCritical(val, 0);
            if (str) {
                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);
        }
    }
}

主要步驟:
1.找到c++的Parcel對象
2.env獲取到string值
3.調用Parcel的writeString方法寫入數據
4.釋放String

/frameworks/native/libs/binder/Parcel.cpp

status_t Parcel::writeString16(const std::unique_ptr<String16>& str)
{
    if (!str) {
        return writeInt32(-1);
    }

    return writeString16(*str);
}

status_t Parcel::writeString16(const String16& str)
{
    return writeString16(str.string(), str.size());
}

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;
}

主要步驟:
1.先寫入長度值
2.調用writeInplace進行補位操作,確保是4的倍數
3.調用memcpy將str複製到data中
4.結束寫入0

(4) Parcel的讀取

直接來看對String類型進行讀取,我們不看映射直接來看操作方法

/frameworks/base/core/jni/android_os_Parcel.cpp
static jstring android_os_Parcel_readString(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        size_t len;
        const char16_t* str = parcel->readString16Inplace(&len);
        if (str) {
            return env->NewString(reinterpret_cast<const jchar*>(str), len);
        }
        return NULL;
    }
    return NULL;
}

主要步驟:
1.獲取c++層Parcel對象
2.通過Parcel的readString16Inplace方法讀取,獲取到長度和字符串
3.轉換爲string返回

/frameworks/native/libs/binder/Parcel.cpp

const char16_t* Parcel::readString16Inplace(size_t* outLen) const
{
    int32_t size = readInt32();
    // watch for potential int overflow from size+1
    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;
}



主要步驟:
1.讀取字符串長度
2.通過readInplace讀取值,由於最後寫入了0所以需要+1
3.讀取完成返回

const void* Parcel::readInplace(size_t len) const
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return NULL;
    }

    if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
            && len <= pad_size(len)) {
        if (mObjectsSize > 0) {
            status_t err = validateReadData(mDataPos + pad_size(len));
            if(err != NO_ERROR) {
                // Still increment the data position by the expected length
                mDataPos += pad_size(len);
                ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos);
                return NULL;
            }
        }

        const void* data = mData+mDataPos;
        mDataPos += pad_size(len);
        ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos);
        return data;
    }
    return NULL;
}

根據長度以及補位相關,計算得到指針位置mDataPos,並返回指針,完成整個讀取。

小結:相對於Serializable,Parcelable在序列化過程中,產生的臨時變量很少,也沒用到反射。



三、Serializable和Parcelable對比

- Serializable Parcelable
變量數 序列化過程中產生大量臨時變量 少量
反射 使用 未使用
實現難易度 相對簡單 相對難一點
緩存方式 適用於所有場景 不適用於磁盤等持久化
內存消耗
讀寫數據形式 IO流讀取 內存直接讀取
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章