Adroid读代码 Parcel - (1)

2018-08-27  本文已影响0人  tjy_2011

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

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

上一篇下一篇

猜你喜欢

热点阅读