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;
}
}
}
}
这是最简单的部分,后面待续。。
网友评论