深入c++層分析Serializable和Parcelable的區別
文章目錄
- 深入c++層分析Serializable和Parcelable的區別
- 一、Serializable的源碼分析
- 1、首先來看一下ObjectOutputStream產生的中間變量
- 2、序列化過程
- (1)有多個構造函數,這裏選取一個public來分析
- (2)verifySubclass方法
- (3)再來看writeStreamHeader方法,寫入了哪些頭信息
- (4)通常我們構造了ObjectOutputStream oos = new ObjectOutputStream(out)後,會調用writeObject(Object)來寫入對象
- (5)writeOrdinaryObject
- (6)寫入描述信息
- (8)寫入字段信息
- (9)舉幾個基礎類型寫入
- (10)BlockDataOutputStream類,在這裏我就不分析了,喜歡深入瞭解的朋友,可以繼續查看BlockDataOutputStream的相關write*方法
- 一.Parcelable的源碼分析
- 三、Serializable和Parcelable對比
一、Serializable的源碼分析
Serializable是爲了保存對象的屬性到本地文件、網絡傳輸等方便數據傳輸,由於對應使用了ObjectInputStream和ObjectOutputStream進行讀寫,所以在讀取過程中會使用了反射以及產生過多的臨時變量。
由於Serializable的性能過低,不太適用於性能資源相對緊張的嵌入式設備應用間傳遞數據,所以Android設計了新序列化方式–Parcelable用於內存間傳遞數據
接下來我們從ObjectOutputStream讀取數據來分析爲什麼消耗過多資源
1、首先來看一下ObjectOutputStream產生的中間變量
public class ObjectOutputStream
extends OutputStream implements ObjectOutput, ObjectStreamConstants
{
private static class Caches {
/** 子類安全性審覈結果的緩存 */
static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
new ConcurrentHashMap<>();
/** 將WeakReferences引用到審覈的子類中 */
static final ReferenceQueue<Class<?>> subclassAuditsQueue =
new ReferenceQueue<>();
}
/** 用於處理塊數據轉換的過濾流,是ObjectOutputStream的內部類,實際將數據寫入的類 */
private final BlockDataOutputStream bout;
/** 後面會構造一個大小爲10,負載因子爲3的HandleTable和ReplaceTable */
private final HandleTable handles;
private final ReplaceTable subs;
/** 流的協議版本 */
private int protocol = PROTOCOL_VERSION_2;
/** 遞歸深度 */
private int depth;
/** 用於寫入基本類型字段值的緩衝區 */
private byte[] primVals;
/** 如果true,子類重寫writeObjectOverride()處理,否則writeObject()處理 */
private final boolean enableOverride;
/** 如果true,調用replaceObject() */
private boolean enableReplace;
// 下面的值只在上行調用writeObject()/writeExternal()時有效
/**
* 上行調用類定義的writeObject方法時的上下文,持有當前被序列化的對象和當前對象描述符。在非 writeObject上行調用時爲null
*/
private SerialCallbackContext curContext;
/** 當前PutField對象 */
private PutFieldImpl curPut;
/** 常規存儲用於debug棧信息 */
private final DebugTraceInfoStack debugInfoStack;
…………
}
上面只是一些成員變量,還不包含局部變量就已經產生了變量來緩存數據,如果序列化對象的層級和深度加深,好用的資源可想而知,下面將會挑選重點來闡述序列化的過程
2、序列化過程
(1)有多個構造函數,這裏選取一個public來分析
public ObjectOutputStream(OutputStream out) throws IOException {
//驗證子類是否違反安全約束
verifySubclass();
//構造一個BlockDataOutputStream用於向out寫入序列化數據,實際寫入的類對象
bout = new BlockDataOutputStream(out);
//構造一個大小爲10,負載因子爲3的HandleTable
handles = new HandleTable(10, (float) 3.00);
//構造一個大小爲10,負載因子爲3的ReplaceTable
subs = new ReplaceTable(10, (float) 3.00);
//恆爲false,除非子類調用protected構造方法
enableOverride = false;
//向流的頭裏寫入魔數和版本號
writeStreamHeader();
//將緩存模式打開,寫入數據時先寫入緩衝區
bout.setBlockDataMode(true);
//是否開啓debug信息棧
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
(2)verifySubclass方法
private void verifySubclass() {
Class<?> cl = getClass();
//如果是當前類,直接返回
if (cl == ObjectOutputStream.class) {
return;
}
//獲取安全管理器是否有對應的權限,沒有則返回
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return;
}
//從弱引用隊列中出隊所有類,並移除緩存中相同的類
processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
//將ObjectOutputStream的子類存入Caches類的對象中
WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
Boolean result = Caches.subclassAudits.get(key);
if (result == null) {
//檢查這個子類是否安全
result = Boolean.valueOf(auditSubclass(cl));
//將結果存儲到緩存
Caches.subclassAudits.putIfAbsent(key, result);
}
if (result.booleanValue()) {
return;
}
//驗證是否有實現權限, 如果沒有權限則拋出SecurityException異常
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
(3)再來看writeStreamHeader方法,寫入了哪些頭信息
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC); //寫入兩個字節:0xAC和0xED
bout.writeShort(STREAM_VERSION); //寫入兩個字節:0x00和0x05
}
(4)通常我們構造了ObjectOutputStream oos = new ObjectOutputStream(out)後,會調用writeObject(Object)來寫入對象
public final void writeObject(Object obj) throws IOException {
//是否子類重寫了,如果重寫,交由子類的writeObjectOverride處理
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
//寫入對象
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
//安卓實現中使用try-catch忽略了次要的一些異常
try {
writeFatalException(ex);
} catch (IOException ex2) {
}
}
throw ex;
}
}
/**
* 基礎writeObject / writeUnshared實現。
**/
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
//關閉緩存模式
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// 處理以前寫過的和不可替換的對象
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
// 安卓改變 start: 使Class和ObjectStreamClass可替換
/*
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
*/
// 安卓改變 end: 使Class和ObjectStreamClass可替換
}
//檢查可替換的對象
Object orig = obj;
Class<?> cl = obj.getClass();
//序列化對象對應的Class對象的詳細信息
ObjectStreamClass desc;
// BEGIN 安卓改變: 僅調用一次writeReplace,在java中for循環中判斷始終爲true,不需要循環
/*
for (;;) {
// REMIND: 對於strings/arrays跳過檢查?
Class<?> repCl;
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
desc = ObjectStreamClass.lookup(cl, true);
break;
}
*/
// 只替換一次通過
Class repCl;
//獲取序列化對象對應的Class對象詳細信息
desc = ObjectStreamClass.lookup(cl, true);
if (desc.hasWriteReplaceMethod() &&
(obj = desc.invokeWriteReplace(obj)) != null &&
(repCl = obj.getClass()) != cl)
{
cl = repCl;
desc = ObjectStreamClass.lookup(cl, true);
}
// END 安卓改變: 僅調用一次writeReplace,在java中for循環中判斷始終爲true,不需要循環
if (enableReplace) {
//replaceObject用來替換這個對象進行序列化,默認實現爲空,一般用於子類重寫實現序列化的定製
Object rep = replaceObject(obj);
//如果對象被替換了
if (rep != obj && rep != null) {
cl = rep.getClass();
//重新查找對應的ObjectStreamClass
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
}
// 如果對象被替換,則再次運行原始檢查
if (obj != orig) {
subs.assign(orig, obj);
if (obj == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
// BEGIN 安卓改變:使Class和ObjectStreamClass可替換
/*
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
*/
// END 安卓改變:使Class和ObjectStreamClass可替換
}
}
// remaining cases
// BEGIN 安卓改變: 使Class和ObjectStreamClass可替換
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END 安卓改變: 使Class和ObjectStreamClass可替換.
//序列化對象類型爲String、數組、枚舉時,調用定製的寫入方法
} else if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
//一般對象類型的寫入,這裏就是需要實現Serializable的對象寫入的地方
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
//如果沒有實現序列化接口會拋出異常
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
//結束方法前將方法棧深減去1
depth--;
//切換回之前的緩存模式
bout.setBlockDataMode(oldMode);
}
}
(5)writeOrdinaryObject
/**
* 最終會以一種遞歸的形式寫入對象信息。
**/
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc,
boolean unshared) throws IOException {
if (extendedDebugInfo)
debugInfoStack.push(
(depth == 1 ? "root " : "") + "object (class \"" +
obj.getClass().getName() + "\", " + obj.toString() + ")");
try {
//檢查ObjectStreamClass對象
desc.checkSerialize();
//寫入字節0x73
bout.writeByte(TC_OBJECT);
//寫入對應的Class對象的信息
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);//如果是share模式把這個對象加入緩存
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
//寫入這個對象變量信息及其父類的成員變量
//將這個實例及其父類基本數據類型寫入文件,如果檢測到有引用類型,那麼會繼續調用
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
(6)寫入描述信息
/**
* 將給定類描述符的表示形式寫入流。
*/
private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
int handle;
if (desc == null) {
//描述爲空,寫null
writeNull();
} else if (!unshared && (handle = handles.lookup(desc)) != -1) {
//共享模式且緩存中已有該類描述符時,寫對應句柄值
writeHandle(handle);
} else if (desc.isProxy()) {
//描述信息爲動態代理類寫入
writeProxyDesc(desc, unshared);
} else {
//標準類寫入
writeNonProxyDesc(desc, unshared);
}
}
private boolean isCustomSubclass() {
// 如果此類是ObjectOutputStream的自定義子類,則返回true
return getClass().getClassLoader()
!= ObjectOutputStream.class.getClassLoader();
}
/**
* 將表示動態代理類的類描述符寫入流中。
*/
private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
bout.writeByte(TC_PROXYCLASSDESC);
//共享則寫入緩存
handles.assign(unshared ? null : desc);
//獲取類實現的接口,然後寫入接口個數和接口名。此處使用反射
Class<?> cl = desc.forClass();
Class<?>[] ifaces = cl.getInterfaces();
bout.writeInt(ifaces.length);
//寫入接口名
for (int i = 0; i < ifaces.length; i++) {
bout.writeUTF(ifaces[i].getName());
}
//打開緩存模式
bout.setBlockDataMode(true);
//如果是子類,校驗包權限。此處使用反射
if (cl != null && isCustomSubclass()) {
ReflectUtil.checkPackageAccess(cl);
}
//裝配動態代理類,子類可以重寫這個方法存儲類信息到流中,默認什麼也不做
annotateProxyClass(cl);
//關閉緩存模式
bout.setBlockDataMode(false);
//寫入塊結束符
bout.writeByte(TC_ENDBLOCKDATA);
//接下來再遞歸寫入父類信息
writeClassDesc(desc.getSuperDesc(), false);
}
/**
* 將代表標準(即不是動態代理)類的類描述符寫入流中。
*/
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
throws IOException
{
bout.writeByte(TC_CLASSDESC);
handles.assign(unshared ? null : desc);
if (protocol == PROTOCOL_VERSION_1) {
// 不要使用舊協議調用類描述符寫鉤子
desc.writeNonProxy(this);
} else {
writeClassDescriptor(desc);
}
Class<?> cl = desc.forClass();
bout.setBlockDataMode(true);
if (cl != null && isCustomSubclass()) {
ReflectUtil.checkPackageAccess(cl);
}
//子類可以重寫這個方法存儲類信息到流中,默認什麼也不做
annotateClass(cl);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
//接下來再遞歸寫入父類信息
writeClassDesc(desc.getSuperDesc(), false);
}
(8)寫入字段信息
/**
* 通過調用其writeExternal()方法來寫入給定對象的可外部化數據。
*/
private void writeExternalData(Externalizable obj) throws IOException {
PutFieldImpl oldPut = curPut;
curPut = null;
if (extendedDebugInfo) {
debugInfoStack.push("writeExternal data");
}
//存儲上下文
SerialCallbackContext oldContext = curContext;
try {
curContext = null;
if (protocol == PROTOCOL_VERSION_1) {
//協議版本1
obj.writeExternal(this);
} else {
//默認協議是2,所以會使用塊輸出流
bout.setBlockDataMode(true);
//取決於類的實現
obj.writeExternal(this);
bout.setBlockDataMode(false);
//寫入塊數據結束符
bout.writeByte(TC_ENDBLOCKDATA);
}
} finally {
//將上下文改變回來
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
}
/**
* 爲給定對象的每個可序列化類(從超類到子類)寫入實例數據。
*/
private void writeSerialData(Object obj, ObjectStreamClass desc)
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
//重寫了writeObject方法
if (slotDesc.hasWriteObjectMethod()) {
PutFieldImpl oldPut = curPut;
curPut = null;
SerialCallbackContext oldContext = curContext;
if (extendedDebugInfo) {
debugInfoStack.push(
"custom writeObject data (class \"" +
slotDesc.getName() + "\")");
}
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bout.setBlockDataMode(true);
slotDesc.invokeWriteObject(obj, this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
} else {
//如果沒有重寫writeObject則輸出默認內容
defaultWriteFields(obj, slotDesc);
}
}
}
/**
* 獲取並寫入給定對象的可序列化字段的值以流式傳輸。 給定的類描述符指定要寫入的字段值,以及應按什麼順序寫入它們。
*/
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
throws IOException
{
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
desc.checkDefaultSerialize();
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
//將基本類型數據的字段值存入緩衝區
desc.getPrimFieldValues(obj, primVals);
//輸出緩衝區內容
bout.write(primVals, 0, primDataSize, false);
ObjectStreamField[] fields = desc.getFields(false);
//獲取非基本數據類型對象
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"field (class \"" + desc.getName() + "\", name: \"" +
fields[numPrimFields + i].getName() + "\", type: \"" +
fields[numPrimFields + i].getType() + "\")");
}
try {
//遞歸繼續寫入
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
(9)舉幾個基礎類型寫入
private void writeString(String str, boolean unshared) throws IOException {
handles.assign(unshared ? null : str);
//獲得UTF編碼長度
long utflen = bout.getUTFLength(str);
if (utflen <= 0xFFFF) {
//寫入類型標誌符
bout.writeByte(TC_STRING);
//寫入utf編碼數據
bout.writeUTF(str, utflen);
} else {
//寫入類型標誌符
bout.writeByte(TC_LONGSTRING);
//寫入utf編碼數據
bout.writeLongUTF(str, utflen);
}
}
private void writeEnum(Enum<?> en,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
//寫入類型標誌符
bout.writeByte(TC_ENUM);
ObjectStreamClass sdesc = desc.getSuperDesc();
//寫入類信息
writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
//共享模式寫入緩存
handles.assign(unshared ? null : en);
//以字符串形式寫入name
writeString(en.name(), false);
}
(10)BlockDataOutputStream類,在這裏我就不分析了,喜歡深入瞭解的朋友,可以繼續查看BlockDataOutputStream的相關write*方法
一.Parcelable的源碼分析
Parcelable在性能消耗和內存開銷上都比Serializable要小,所以內存間傳遞數據推薦使用Parcelable。Parcelable不適用於磁盤上存儲數據,由於Parcelable存儲數據是有序的,在磁盤上持久化數據,會隨着對象的不同版本,新增、刪除、交換位置等原因,導致讀取數據異常。
1、編碼實現形式
(1)編寫一個實體類
public class Bean1 implements Parcelable {
private String name;
private int age;
protected Bean1(Parcel in) {
//從Parcel對象讀取反序列化值,必須和寫入保持一致
name = in.readString();
age = in.readInt();
}
public static final Creator<Bean1> CREATOR = new Creator<Bean1>() {
@Override
public Bean1 createFromParcel(Parcel in) {
return new Bean1(in);
}
@Override
public Bean1[] newArray(int size) {
return new Bean1[size];
}
};
@Override
public int describeContents() {
return 0;//默認爲0,如果需要序列化file description,改爲1
}
@Override
public void writeToParcel(Parcel dest, int flags) {
//向Parcel對象寫入數據用於本地化--必須和讀取一致
dest.writeString(name);
dest.writeInt(age);
}
}
2.從編寫aidl看Parcelable序列化過程
(1)編寫aidl
// IBean.aidl
package com.yetao.android.library.sdk28;
// Declare any non-default types here with import statements
interface IBean {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
package com.yetao.android.library.sdk28;
// 在這裏使用import語句聲明所有非默認類型
//android studio自動生成
public interface IBean extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.yetao.android.library.sdk28.IBean {
private static final java.lang.String DESCRIPTOR = "com.yetao.android.library.sdk28.IBean";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.yetao.android.library.sdk28.IBean interface,
* generating a proxy if needed.
*/
public static com.yetao.android.library.sdk28.IBean asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.yetao.android.library.sdk28.IBean))) {
return ((com.yetao.android.library.sdk28.IBean) iin);
}
return new com.yetao.android.library.sdk28.IBean.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
//按順序從data中讀出字段值,必須按順序寫
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.yetao.android.library.sdk28.IBean {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* 演示一些可用作參數的基本類型並以AIDL返回值。
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
//一下是序列化數據的寫入,沒有寫入字段名,所以在讀取的時候必須按順序
//所以不能用於磁盤持久化中,一旦文件字段增刪查改,反序列化將出現問題
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* 演示一些可用作參數的基本類型並以AIDL返回值。
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
(2)Parcel創建
上面介紹了aidl通過Parcel寫入數據來做交互,其實Parcel在java和c++層都對應有具體的操作,java層提供接口,實現在c++層。
先來看java層的Parcel初始化
Parcel.java
/**
* @param nativePtr 是c++層Parcel對應的地址
**/
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) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
//如果沒有傳入,則調用本地方法創建
mNativePtr = nativeCreate();
mOwnsNativeParcelObject = true;
}
}
private static native long nativeCreate();
我們來深入c++層分析是怎麼創建的
// /frameworks/base/core/jni/android_os_Parcel.cpp
//nativeCreate映射到android_os_Parcel_create方法
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
//創建出c++層Parcel對象
Parcel* parcel = new Parcel();
//將指針地址轉換成long型地址返回java層,被mNativePtr持有
return reinterpret_cast<jlong>(parcel);
}
(2)Parcel的寫入
說道寫入我們先來c++層看看Pracel的幾個變量
/frameworks/base/core/jni/android_os_Parcel.cpp
uint8_t* mData;//數據存儲的指針
size_t mDataSize;//數據的大小
size_t mDataCapacity;//數據的容量
mutable size_t mDataPos;//數據存儲的指針位置
後面圍繞着這幾個變量,來對數據進行寫入操作,以writeInt()爲例,還是從java到c++層分析
Parcel.java
/**
* 在當前dataPosition()處將整數值寫入包裹,
* 如果需要,可增加dataCapacity()。
*/
public final void writeInt(int val) {
nativeWriteInt(mNativePtr, val);
}
//傳入c++Parcel對象指針和要寫入的值
private static native void nativeWriteInt(long nativePtr, int val);
上面java層沒有實現具體操作,具體操作還是在c++層實現
/frameworks/base/core/jni/android_os_Parcel.cpp
//將nativeWriteInt方法映射到android_os_Parcel_writeInt
{"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
//從long型指針強轉成Parcel指針
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
//實際調用parcel的writeInt32方法寫入
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
jni層調用Parcel的writeInt32(val)來寫入值
/frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
template<class T>
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
//檢查當前是否大於容量,如果大於就擴容,之後直接跳轉到寫入
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
*reinterpret_cast<T*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
}
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
writeAligned主要做了以下幾步:
1.判斷寫入容量是否足夠
2.不夠的話,先擴容然後再寫入;如果容量夠,直接寫入
3.寫入數據完成後,寫入數據長度。
寫入String類型數據的有點不一樣,來看看如何寫入,直接看c++層
/frameworks/base/core/jni/android_os_Parcel.cpp
//映射方法
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
status_t err = NO_MEMORY;
if (val) {
//用掉writeString16寫入str
const jchar* str = env->GetStringCritical(val, 0);
if (str) {
err = parcel->writeString16(
reinterpret_cast<const char16_t*>(str),
env->GetStringLength(val));
env->ReleaseStringCritical(val, str);
}
} else {
err = parcel->writeString16(NULL, 0);
}
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
主要步驟:
1.找到c++的Parcel對象
2.env獲取到string值
3.調用Parcel的writeString方法寫入數據
4.釋放String
/frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeString16(const std::unique_ptr<String16>& str)
{
if (!str) {
return writeInt32(-1);
}
return writeString16(*str);
}
status_t Parcel::writeString16(const String16& str)
{
return writeString16(str.string(), str.size());
}
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
if (str == NULL) return writeInt32(-1);
//先寫入長度
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
if (data) {
memcpy(data, str, len);
*reinterpret_cast<char16_t*>(data+len) = 0;
return NO_ERROR;
}
err = mError;
}
return err;
}
主要步驟:
1.先寫入長度值
2.調用writeInplace進行補位操作,確保是4的倍數
3.調用memcpy將str複製到data中
4.結束寫入0
(4) Parcel的讀取
直接來看對String類型進行讀取,我們不看映射直接來看操作方法
/frameworks/base/core/jni/android_os_Parcel.cpp
static jstring android_os_Parcel_readString(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
size_t len;
const char16_t* str = parcel->readString16Inplace(&len);
if (str) {
return env->NewString(reinterpret_cast<const jchar*>(str), len);
}
return NULL;
}
return NULL;
}
主要步驟:
1.獲取c++層Parcel對象
2.通過Parcel的readString16Inplace方法讀取,獲取到長度和字符串
3.轉換爲string返回
/frameworks/native/libs/binder/Parcel.cpp
const char16_t* Parcel::readString16Inplace(size_t* outLen) const
{
int32_t size = readInt32();
// watch for potential int overflow from size+1
if (size >= 0 && size < INT32_MAX) {
*outLen = size;
const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
if (str != NULL) {
return str;
}
}
*outLen = 0;
return NULL;
}
主要步驟:
1.讀取字符串長度
2.通過readInplace讀取值,由於最後寫入了0所以需要+1
3.讀取完成返回
const void* Parcel::readInplace(size_t len) const
{
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return NULL;
}
if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize
&& len <= pad_size(len)) {
if (mObjectsSize > 0) {
status_t err = validateReadData(mDataPos + pad_size(len));
if(err != NO_ERROR) {
// Still increment the data position by the expected length
mDataPos += pad_size(len);
ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos);
return NULL;
}
}
const void* data = mData+mDataPos;
mDataPos += pad_size(len);
ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos);
return data;
}
return NULL;
}
根據長度以及補位相關,計算得到指針位置mDataPos,並返回指針,完成整個讀取。
小結:相對於Serializable,Parcelable在序列化過程中,產生的臨時變量很少,也沒用到反射。
三、Serializable和Parcelable對比
- | Serializable | Parcelable |
---|---|---|
變量數 | 序列化過程中產生大量臨時變量 | 少量 |
反射 | 使用 | 未使用 |
實現難易度 | 相對簡單 | 相對難一點 |
緩存方式 | 適用於所有場景 | 不適用於磁盤等持久化 |
內存消耗 | 多 | 少 |
讀寫數據形式 | IO流讀取 | 內存直接讀取 |