美文网首页
Adroid读代码 Parcel - (1)

Adroid读代码 Parcel - (1)

作者: tjy_2011 | 来源:发表于2018-08-27 22:20 被阅读0次

    Adroid读代码 Parcel - (1)

    关键字:Android, Parcel

    20180824 tjy

    转载请注明出处

    从今天开始写Android Parcel的代码。Android代码使用http://androidxref.com/8.1.0_r33/上面的代码。

    我觉得有必要解释下读代码的方式。
    比较简单的解释直接用注释的方式写在代码里面;
    如果代码有深层次的方法调用或者横跨Java和C++,会直接在函数下面列出来调用的函数的代码以及目录文件位置,同时会注明是C++代码还是Java代码。这样的好处是传递的参数的值能一目了然,缺点是不适合写在网页里面,因为有时候调用会很深,并且有代码使用了goto语句,破坏了从上到下的调用结构。
    不过,这里是网页,所以我会尽可能的把代码分开来解释。

    Let's go

    先从 Parcel.java开始。
    Parcel.java 是Parcel在Java的描述,其实现用C++在Parcel.cpp描述,Java描述和C++描述通过JNI连接。

    Parcel.java有很长的英文注释,作者写的这么仔细,不研读下岂不可惜。

    在注释里面,作者说Parcel是一个data 和 object reference 的容器,能够在IBinder之间进行传递,
    这是Parcel最主要的作用。作者强调Parcel不是一个通用的序列化机制,不能把数据放入Parcel后将其持久化。

    //http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/os/Parcel.java
    /**
     * Container for a message (data and object references) that can
     * be sent through an IBinder.  A Parcel can contain both flattened data
     * that will be unflattened on the other side of the IPC (using the various
     * methods here for writing specific types, or the general
     * {@link Parcelable} interface), and references to live {@link IBinder}
     * objects that will result in the other side receiving a proxy IBinder
     * connected with the original IBinder in the Parcel.
     *
     * <p class="note">Parcel is <strong>not</strong> a general-purpose
     * serialization mechanism.  This class (and the corresponding
     * {@link Parcelable} API for placing arbitrary objects into a Parcel) is
     * designed as a high-performance IPC transport.  As such, it is not
     * appropriate to place any Parcel data in to persistent storage: changes
     * in the underlying implementation of any of the data in the Parcel can
     * render older data unreadable.</p>
     *
     * <p>The bulk of the Parcel API revolves around reading and writing data
     * of various types.  There are six major classes of such functions available.</p>
     *
     * <h3>Primitives</h3>
     *
     * <p>The most basic data functions are for writing and reading primitive
     * data types: {@link #writeByte}, {@link #readByte}, {@link #writeDouble},
     * {@link #readDouble}, {@link #writeFloat}, {@link #readFloat}, {@link #writeInt},
     * {@link #readInt}, {@link #writeLong}, {@link #readLong},
     * {@link #writeString}, {@link #readString}.  Most other
     * data operations are built on top of these.  The given data is written and
     * read using the endianess of the host CPU.</p>
     *
     * <h3>Primitive Arrays</h3>
     *
     * <p>There are a variety of methods for reading and writing raw arrays
     * of primitive objects, which generally result in writing a 4-byte length
     * followed by the primitive data items.  The methods for reading can either
     * read the data into an existing array, or create and return a new array.
     * These available types are:</p>
     *
     * <ul>
     * <li> {@link #writeBooleanArray(boolean[])},
     * {@link #readBooleanArray(boolean[])}, {@link #createBooleanArray()}
     * <li> {@link #writeByteArray(byte[])},
     * {@link #writeByteArray(byte[], int, int)}, {@link #readByteArray(byte[])},
     * {@link #createByteArray()}
     * <li> {@link #writeCharArray(char[])}, {@link #readCharArray(char[])},
     * {@link #createCharArray()}
     * <li> {@link #writeDoubleArray(double[])}, {@link #readDoubleArray(double[])},
     * {@link #createDoubleArray()}
     * <li> {@link #writeFloatArray(float[])}, {@link #readFloatArray(float[])},
     * {@link #createFloatArray()}
     * <li> {@link #writeIntArray(int[])}, {@link #readIntArray(int[])},
     * {@link #createIntArray()}
     * <li> {@link #writeLongArray(long[])}, {@link #readLongArray(long[])},
     * {@link #createLongArray()}
     * <li> {@link #writeStringArray(String[])}, {@link #readStringArray(String[])},
     * {@link #createStringArray()}.
     * <li> {@link #writeSparseBooleanArray(SparseBooleanArray)},
     * {@link #readSparseBooleanArray()}.
     * </ul>
     *
     * <h3>Parcelables</h3>
     *
     * <p>The {@link Parcelable} protocol provides an extremely efficient (but
     * low-level) protocol for objects to write and read themselves from Parcels.
     * You can use the direct methods {@link #writeParcelable(Parcelable, int)}
     * and {@link #readParcelable(ClassLoader)} or
     * {@link #writeParcelableArray} and
     * {@link #readParcelableArray(ClassLoader)} to write or read.  These
     * methods write both the class type and its data to the Parcel, allowing
     * that class to be reconstructed from the appropriate class loader when
     * later reading.</p>
     *
     * <p>There are also some methods that provide a more efficient way to work
     * with Parcelables: {@link #writeTypedObject}, {@link #writeTypedArray},
     * {@link #writeTypedList}, {@link #readTypedObject},
     * {@link #createTypedArray} and {@link #createTypedArrayList}.  These methods
     * do not write the class information of the original object: instead, the
     * caller of the read function must know what type to expect and pass in the
     * appropriate {@link Parcelable.Creator Parcelable.Creator} instead to
     * properly construct the new object and read its data.  (To more efficient
     * write and read a single Parcelable object that is not null, you can directly
     * call {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
     * {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel}
     * yourself.)</p>
     *
     * <h3>Bundles</h3>
     *
     * <p>A special type-safe container, called {@link Bundle}, is available
     * for key/value maps of heterogeneous values.  This has many optimizations
     * for improved performance when reading and writing data, and its type-safe
     * API avoids difficult to debug type errors when finally marshalling the
     * data contents into a Parcel.  The methods to use are
     * {@link #writeBundle(Bundle)}, {@link #readBundle()}, and
     * {@link #readBundle(ClassLoader)}.
     *
     * <h3>Active Objects</h3>
     *
     * <p>An unusual feature of Parcel is the ability to read and write active
     * objects.  For these objects the actual contents of the object is not
     * written, rather a special token referencing the object is written.  When
     * reading the object back from the Parcel, you do not get a new instance of
     * the object, but rather a handle that operates on the exact same object that
     * was originally written.  There are two forms of active objects available.</p>
     *
     * <p>{@link Binder} objects are a core facility of Android's general cross-process
     * communication system.  The {@link IBinder} interface describes an abstract
     * protocol with a Binder object.  Any such interface can be written in to
     * a Parcel, and upon reading you will receive either the original object
     * implementing that interface or a special proxy implementation
     * that communicates calls back to the original object.  The methods to use are
     * {@link #writeStrongBinder(IBinder)},
     * {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
     * {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
     * {@link #createBinderArray()},
     * {@link #writeBinderList(List)}, {@link #readBinderList(List)},
     * {@link #createBinderArrayList()}.</p>
     *
     * <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
     * can be written and {@link ParcelFileDescriptor} objects returned to operate
     * on the original file descriptor.  The returned file descriptor is a dup
     * of the original file descriptor: the object and fd is different, but
     * operating on the same underlying file stream, with the same position, etc.
     * The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
     * {@link #readFileDescriptor()}.
     *
     * <h3>Untyped Containers</h3>
     *
     * <p>A final class of methods are for writing and reading standard Java
     * containers of arbitrary types.  These all revolve around the
     * {@link #writeValue(Object)} and {@link #readValue(ClassLoader)} methods
     * which define the types of objects allowed.  The container methods are
     * {@link #writeArray(Object[])}, {@link #readArray(ClassLoader)},
     * {@link #writeList(List)}, {@link #readList(List, ClassLoader)},
     * {@link #readArrayList(ClassLoader)},
     * {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)},
     * {@link #writeSparseArray(SparseArray)},
     * {@link #readSparseArray(ClassLoader)}.
     */
    

    先看构造函数。
    构造函数接受一个 long 参数,也可以传入0。由于底层使用C++实现,这个参数表示C++ 的 Parcel指针,传入非0值表示底层用这个参数代表的C++的Parcel对象,传入0表示重新创建C++的Parcel*对象。
    下面在代码中用注释的方式简单作了下解释。

    //from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/os/Parcel.java#3048
    
    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) {
            /*
                传入非0值,直接用这个值赋值给mNativePtr,
                后续使用这个mNativePtr操作C++的Parcel对象。
                传入1运行会发生什么?不过这个构造函数是private。
            */
            mNativePtr = nativePtr;
            mOwnsNativeParcelObject = false;
        } else {
            /*
                传入0值,会调用底层的C++代码创建C++ Parcel对象。
                mNativePtr保存C++ Parcel对象指针。
                用mOwnsNativeParcelObject作标记。
            */
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }
    
    //下面代码是C++代码
    //from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/jni/android_os_Parcel.cpp#816
     {"nativeCreate",              "()J", (void*)android_os_Parcel_create},
    
    //from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_create
    static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
    {
        Parcel* parcel = new Parcel();
        /*
            下面有个指针的转换,把Parcel*转成jlong,传递到Java空间。
        */
        return reinterpret_cast<jlong>(parcel);
    }
    

    既然构造函数是private,肯定有public创建Parcel对象的方法,就是obtain方法。
    这里使用了6个元素的池子来重复利用Parcel对象,避免了多次创建和销毁的开销。
    当调用obtain的时候,如果池子里面有可用的对象,则直接返回这个可用对象;如果池子里面现成的对象用完了,这个时候才会创建Parcel对象。

    //from http://androidxref.com/8.1.0_r33/xref/frameworks/base/core/java/android/os/Parcel.java
     
     /*
        定义池子大小和元素池
     */
     private static final int POOL_SIZE = 6;
     private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
     private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
    
    /**
     * Retrieve a new Parcel object from the pool.
     */
    public static Parcel obtain() {
        final Parcel[] pool = sOwnedPool;
        /*
            由于sOwnedPool属性是static,
            所以这个synchronized是类级别的锁,是多线程安全的。
            这里的synchronized (pool)和synchronized (sOwnedPool)是没有区别的。
        */
        synchronized (pool) {
            Parcel p;
            for (int i=0; i<POOL_SIZE; i++) {
                p = pool[i];//从池子里面拿出来一个对象
                if (p != null) {//对象不为空表示对象可用
                    /*
                        pool[i]的引用指向null,
                        其他obtain调用者就无法“看到”p引用的对象了。
                    */
                    pool[i] = null;
                    if (DEBUG_RECYCLE) {
                        p.mStack = new RuntimeException();
                    }
                    //ReadWriteHelper.DEFAULT是ReadWriteHelper的单例static对象
                    p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                    return p;//返回p
                }
            }
        }
        /*
            如果找遍了池子都没有找到可用的对象,则创建一个新的Parcel对象。
        */
        return new Parcel(0);
    }
    
    

    池子也会回收不用的对象。recycle方法做这件事情,在注释里面,作者说,一旦调用recycle方法,就不能再“碰”这个对象了,读也不行。

    
    /**
     * Put a Parcel object back into the pool.  You must not touch
     * the object after this call.
     */
    public final void recycle() {
        if (DEBUG_RECYCLE) mStack = null;
        /*
            freeBuffer方法把C++的数据清空(写入Parcel的数据存在C++代码里面)。
            并重置一些状态。
            后面再来看这个方法。
        */
        freeBuffer();
    
        final Parcel[] pool;
        /*
            在构造函数里面,如果是自己创建的C++对象,
            则mOwnsNativeParcelObject=true。
            这里会检查mOwnsNativeParcelObject的值,
            判断是自己创建的C++对象还是传递过来的C++对象指针。
        */
        if (mOwnsNativeParcelObject) {
            //自己创建的C++对象
            pool = sOwnedPool;
        } else {
            //传递过来的C++对象指针
            mNativePtr = 0;
            pool = sHolderPool;
        }
    
        synchronized (pool) {
            for (int i=0; i<POOL_SIZE; i++) {
                if (pool[i] == null) {//pool[i] == null表示pool[i]是一个空的”萝卜坑“
                //把对象放入池子的“萝卜坑”里(获取被回收对象的引用)
                    pool[i] = this;
                    return;
                }
            }
        }
    }
    

    这是最简单的部分,后面待续。。

    相关文章

      网友评论

          本文标题:Adroid读代码 Parcel - (1)

          本文链接:https://www.haomeiwen.com/subject/bakxwftx.html