Adroid读代码 Parcel - (1)

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.
 *
 * 

Parcel is not 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.

* *

The bulk of the Parcel API revolves around reading and writing data * of various types. There are six major classes of such functions available.

* *

Primitives

* *

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.

* *

Primitive Arrays

* *

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:

* *
    *
  • {@link #writeBooleanArray(boolean[])}, * {@link #readBooleanArray(boolean[])}, {@link #createBooleanArray()} *
  • {@link #writeByteArray(byte[])}, * {@link #writeByteArray(byte[], int, int)}, {@link #readByteArray(byte[])}, * {@link #createByteArray()} *
  • {@link #writeCharArray(char[])}, {@link #readCharArray(char[])}, * {@link #createCharArray()} *
  • {@link #writeDoubleArray(double[])}, {@link #readDoubleArray(double[])}, * {@link #createDoubleArray()} *
  • {@link #writeFloatArray(float[])}, {@link #readFloatArray(float[])}, * {@link #createFloatArray()} *
  • {@link #writeIntArray(int[])}, {@link #readIntArray(int[])}, * {@link #createIntArray()} *
  • {@link #writeLongArray(long[])}, {@link #readLongArray(long[])}, * {@link #createLongArray()} *
  • {@link #writeStringArray(String[])}, {@link #readStringArray(String[])}, * {@link #createStringArray()}. *
  • {@link #writeSparseBooleanArray(SparseBooleanArray)}, * {@link #readSparseBooleanArray()}. *
* *

Parcelables

* *

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.

* *

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.)

* *

Bundles

* *

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)}. * *

Active Objects

* *

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.

* *

{@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()}.

* *

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()}. * *

Untyped Containers

* *

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(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

池子也会回收不用的对象。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

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

你可能感兴趣的:(Adroid读代码 Parcel - (1))