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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容