序列化相关知识点

主要是由于有以下几个疑问:

1、为什么Android里面Activity之间传值, Parcelable比Serializable效率要高?
2、Activity之间通过Intent传值, 大小为什么会有限制? 限制在多少?

关于Activity启动, 其实都知道, 最终会通过ActivityManagerProxy.startActivity然后将消息发送给system_server进程, 然后直接进入到AMP.startActivity方法内部;

ActivityManagerProxy.startActivity
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    // intent将携带的数据写入到Parcel中; 
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    // mRemote实际指向的是BinderProxy对象
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    ...
    return result;
}

一、关于Parcelable与Serializable

1.1 Intent.writeToParcel
public void writeToParcel(Parcel out, int flags) {
    ...
    // intent.putXXX最终都是将数据写入到mExtras中, 现在又将mExtras数据写入到Parcel中;
    out.writeBundle(mExtras);
}
1.2 Parcel.writeBundle
public final void writeBundle(Bundle val) {
    val.writeToParcel(this, 0);
}
1.3 Bundle.writeToParcel
@Override
public void writeToParcel(Parcel parcel, int flags) {
    final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
    try {
        super.writeToParcelInner(parcel, flags);
    } finally {
        parcel.restoreAllowFds(oldAllowFds);
    }
}
void writeToParcelInner(Parcel parcel, int flags) {
    ...
    // map数据被写入到Parcel中;
    parcel.writeArrayMapInternal(map);
}
1.4 Parcel.writeArrayMapInternal
void writeArrayMapInternal(ArrayMap<String, Object> val) {
    final int N = val.size();
    writeInt(N);
    int startPos;
    // 遍历val, 将写入数据到parcel中
    for (int i=0; i<N; i++) {
        writeString(val.keyAt(i));
        writeValue(val.valueAt(i));
    }
}
1.5 Parcel.writeValue
public final void writeValue(Object v) {
    if (v == null) {
        writeInt(VAL_NULL);
    } else if (v instanceof String) {
        writeInt(VAL_STRING);
        writeString((String) v);
    } else if (v instanceof Integer) {
        writeInt(VAL_INTEGER);
        writeInt((Integer) v);
    } else if (v instanceof Map) {
        writeInt(VAL_MAP);
        writeMap((Map) v);
    } else if (v instanceof Bundle) {
        writeInt(VAL_BUNDLE);
        writeBundle((Bundle) v);
    } else if (v instanceof PersistableBundle) {
        writeInt(VAL_PERSISTABLEBUNDLE);
        writePersistableBundle((PersistableBundle) v);
    } else if (v instanceof Parcelable) {// Parcelable类型
        writeInt(VAL_PARCELABLE);
        writeParcelable((Parcelable) v, 0);
    } else if (v instanceof Short) {
        writeInt(VAL_SHORT);
        writeInt(((Short) v).intValue());
    } else if (v instanceof Long) {
        writeInt(VAL_LONG);
        writeLong((Long) v);
    } else if (v instanceof Float) {
        writeInt(VAL_FLOAT);
        writeFloat((Float) v);
    } else if (v instanceof Double) {
        writeInt(VAL_DOUBLE);
        writeDouble((Double) v);
    } else if (v instanceof Boolean) {
        writeInt(VAL_BOOLEAN);
        writeInt((Boolean) v ? 1 : 0);
    } else if (v instanceof CharSequence) {
        writeInt(VAL_CHARSEQUENCE);
        writeCharSequence((CharSequence) v);
    } else if (v instanceof List) {
        writeInt(VAL_LIST);
        writeList((List) v);
    } else if (v instanceof SparseArray) {
        writeInt(VAL_SPARSEARRAY);
        writeSparseArray((SparseArray) v);
    } else if (v instanceof boolean[]) {
        writeInt(VAL_BOOLEANARRAY);
        writeBooleanArray((boolean[]) v);
    } else if (v instanceof byte[]) {
        writeInt(VAL_BYTEARRAY);
        writeByteArray((byte[]) v);
    } else if (v instanceof String[]) {
        writeInt(VAL_STRINGARRAY);
        writeStringArray((String[]) v);
    } else if (v instanceof CharSequence[]) {
        // Must be after String[] and before Object[]
        writeInt(VAL_CHARSEQUENCEARRAY);
        writeCharSequenceArray((CharSequence[]) v);
    } else if (v instanceof IBinder) {
        writeInt(VAL_IBINDER);
        writeStrongBinder((IBinder) v);
    } else if (v instanceof Parcelable[]) {
        writeInt(VAL_PARCELABLEARRAY);
        writeParcelableArray((Parcelable[]) v, 0);
    } else if (v instanceof int[]) {
        writeInt(VAL_INTARRAY);
        writeIntArray((int[]) v);
    } else if (v instanceof long[]) {
        writeInt(VAL_LONGARRAY);
        writeLongArray((long[]) v);
    } else if (v instanceof Byte) {
        writeInt(VAL_BYTE);
        writeInt((Byte) v);
    } else if (v instanceof Size) {
        writeInt(VAL_SIZE);
        writeSize((Size) v);
    } else if (v instanceof SizeF) {
        writeInt(VAL_SIZEF);
        writeSizeF((SizeF) v);
    } else if (v instanceof double[]) {
        writeInt(VAL_DOUBLEARRAY);
        writeDoubleArray((double[]) v);
    } else {
        Class<?> clazz = v.getClass();
        if (clazz.isArray() && clazz.getComponentType() == Object.class) {
            // Only pure Object[] are written here, Other arrays of non-primitive types are
            // handled by serialization as this does not record the component type.
            writeInt(VAL_OBJECTARRAY);
            writeArray((Object[]) v);
        } else if (v instanceof Serializable) {// Serializable类型
            // Must be last
            writeInt(VAL_SERIALIZABLE);
            writeSerializable((Serializable) v);
        } 
    }
}

  1、从writeValue其实可以很直观的看出来, Parcel在进行写数据时, 除了支持基本数据类型及其数组, 还支持三种引用数据类型: String及其数组, 实现了Parcelable的类及其数组, 以及实现了Serializable的类.
  2、而当V instanceof Parcelable时, 继续调用writeParcelable对V进行序列化数据读写操作.
  3、当V instanceof Serializable时, 通过writeSerializable进行序列化数据读写操作;

1.5.1 Parcel.writeSerializable
public final void writeSerializable(Serializable s) {
    String name = s.getClass().getName();
    writeString(name);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(s);
        oos.close();
        writeByteArray(baos.toByteArray());
    } catch (IOException ioe) {
        throw new RuntimeException("Parcelable encountered " +
                "IOException writing serializable object (name = " + name +
                ")", ioe);
    }
}

  1、从代码可以很直观的看出, 当V instanceof Serializable时, 会先将数据转换成流数据, 然后再进行数据传输, 然后有一个问题, 如果使用Intent传输Serializable类型的数据, 输出方输出的对象与输入方接收到的数据是同一个吗?

public class B implements Serializable {
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this, TestUi.class);
        B b = new B();
        Log.v("AndroidTest", "b:" + b);
        intent.putExtra("B", b);
        startActivity(intent);
    }
}
public class TestUi extends Activity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Serializable b = getIntent().getSerializableExtra("B");
        Log.v("AndroidTest", "b:" + b);
    }
}

打印结果如下:
06-24 12:36:24.648 b:androidtest.myapplication.B@37b040c1
06-24 12:36:24.674 b:androidtest.myapplication.B@2a0ffc9f

总结: 当v instanceof Serializable时, 进程间通信传值时, 首先会将v转换为IO流数据, 然后再进行数据传输操作, 因为Parceable内部只支持基本数据类型及其数组, 以及String类型, 所以v instanceof Parcelable时, 直接进行值的传输. 这样就是为什么进行间通信Parceable效率要比Serializable效率高.

二、关于Intent传值大小限制的问题

ActivityManagerProxy.startActivity

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    // intent将携带的数据写入到Parcel中; 
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    // mRemote实际指向的是BinderProxy对象
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    ...
    return result;
}

mRemote实际指向的是BinderProxy

BinderProxy.transact
public class Binder.BinderProxy {
    public boolean transact(int code, Parcel data, Parcel reply, int flags) {
        // Activity启动时, Intent携带的数据最终被转化到Parcel中, 这里检测data的大小
        // 也就是检测Intent携带的数据的大小;
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        final boolean tracingEnabled = Binder.isTracingEnabled();
        return transactNative(code, data, reply, flags);
    }
}
public class Binder {
    static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
        // 大小限制在800kb
        if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
            // Trying to send > 800k, this is way too much
            StringBuilder sb = new StringBuilder();
            sb.append(msg);
            sb.append(": on ");
            sb.append(obj);
            sb.append(" calling ");
            sb.append(code);
            sb.append(" size ");
            sb.append(parcel.dataSize());
            sb.append(" (data: ");
            parcel.setDataPosition(0);
            sb.append(parcel.readInt());
            sb.append(", ");
            sb.append(parcel.readInt());
            sb.append(", ");
            sb.append(parcel.readInt());
            sb.append(")");
            Slog.wtfStack(TAG, sb.toString());
        }
    }
}

上面代码并没有做什么限制, 仅仅是从应用层角度进行提醒, 传输的数据大小不能超过800kb, 真正的大小限制是在native层, 流程比较复杂, 所以关于Intent传值大小限制, 这里仅仅记住结论.

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

推荐阅读更多精彩内容

  • 1、Intent 可以传递的数据类型 在 Intent和IntentFilters(1) 中我们知道了通过 Int...
    CnPeng阅读 2,788评论 0 10
  • AndroidSmall框架是android中一个非常优秀的插件化框架,有时我们不仅要学会使用它,也要去理解它的原...
    柴犬大人阅读 1,255评论 0 6
  • Activity 组件启动,Intent 类携带目标组件类信息,组件间传递的数据。 复杂类型必须实现 Parcel...
    gczxbb阅读 4,586评论 1 4
  • 段子 公园里,一位仙风鹤骨的老者在打太极,一招一式都仙气十足,一个年轻人走过去:“大爷,太极这玩意儿花拳绣腿,你练...
    大公爵阅读 17,818评论 4 41
  • 【Android】Android中Serializable接口的定义和使用 1、序列化是干什么的? 简单说就是为了...
    zhengzhoufeng阅读 110评论 0 0