什么是序列化
序列化:将数据结构或对象转换成字节序列的过程。
反序列化:将在序列化过程中所生成的字节序列转换成数据结构或者对象的过程。
Serializable与Parcelable的对比
Serializable:
1.Serializable是java提供的可序列化接口
2.Serializable的序列化与反序列化需要大量的IO操作,效率比较低
3.Serializable实现起来很简单,实现Serializable接口就好
Parcelable:
1.Parcelable是Android特有的可序列化接口
2.Parcelable的效率比较高,直接操作内存数据
3.Parcleable实现起来比较复杂
使用场景
1.Parcleable: 内存中的序列化时使用,效率更高
2.Serializable: 对象序列化到存储设备中、在网络中传输等
Serializable
Serializable 要注意以下几点
使用使用Serializabl方式序列化,要在类中添加serialVersionUID,同来标识唯一(兼容与安全,如果父类声明了,则子类不需要声明)。
使用transient关键字标识的成员变量(在序列化后,成员变量的值被设置为初始值,如果成员变量是对象则是null)
静态成员变量属于类不属于对象,所以不会参与序列化(对象序列化保存的是对象的“状态”,也
就是它的成员变量,因此序列化不会关注静态变量)
transient关键字对静态成员变量无效,因为静态变量不参与序列化,所以他即使被transient标识,他的值也不会被修改。
serialVersionUID 与兼容性
serialVersionUID的作用:serialVersionUID 用来表明类的不同版本间的兼容性。如果你修改了此类, 要修改此值。否则以前
用老版本的类序列化的类恢复时会报错: InvalidClassException
设置方式:在JDK中,可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID,对于Test.class,执行命令:serialver Test
JVM 规范强烈 建议我们手动声明一个版本号,这个数字可以是随机的,只要固定不变就可以。同 时最好是private和final的,尽量保证不变。
序列化破坏单例
在单例中添加如下代码:
private Object readResolve() {
return mInstance;
}
反射也会破坏单例,虽然可以通过添加flag去做一些判断,但反射仍然可以创建单例类的实例,因为反射可以拿到和修改一个类的所有信息。而Serializable在序列化过程中使用了反射机制。
Parcelable
Parcelable的使用
public class NameBean implements Parcelable {
private String name;
public NameBean(String name) {
this.name = name;
}
protected NameBean(Parcel in) {
name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator CREATOR = new Creator() {
@Override
public NameBean createFromParcel(Parcel in) {
return new NameBean(in);
}
@Override
public NameBean[] newArray(int size) {
return new NameBean[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后在activity中,
Intent intent = new Intent(MainActivity.this,TestActivity.class);
NameBean bean = new NameBean("zzf");
Log.e("zzf","bean1 :" + bean);
intent.putExtra("bean1",bean);
startActivity(intent);
可以知道在实现Parcelable的时候需要实现实现一个Creator, 用来恢复对象,如果没有实现这个Creator,那么恢复的时候,会报错。
源码分析
首先我们调用putExtra()
public @NonNull Intent putExtra(String name, @Nullable Parcelable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putParcelable(name, value);
return this;
}
在Intent的putExtra会调用Bundle的putParcelable()
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
}
存放在map中。这个map在BaseBundle定义,是ArrayMap。在这里有个疑问,为什么不用hashmap,而是创建一个Bundle?这个问题后续分析。
从上面我们可以看到,在activity中put的数据会先保存在一个map中,那要写入内存中肯定会调用
writeToParcel()。
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) {
final ArrayMap map;
synchronized (this) {
if (mParcelledData != null) {
if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
parcel.writeInt(0);
} else {
int length = mParcelledData.dataSize();
parcel.writeInt(length);
parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
parcel.appendFrom(mParcelledData, 0, length);
}
return;
}
map = mMap;
}
if (map == null || map.size() <= 0) {
parcel.writeInt(0);
return;
}
int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // dummy, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
最后会调用parcel的writeArrayMapInternal()将map写入。
void writeArrayMapInternal(@Nullable ArrayMap val) {
if (val == null) {
writeInt(-1);
return;
}
final int N = val.size();
writeInt(N);
int startPos;
for (int i=0; i
在这个方法中进行对map遍历。然后将值进行写入。
public final void writeValue(@Nullable Object v) {
if (v instanceof Parcelable) {
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);//1
} else if (v instanceof double[]) {
。。。。。
} else {
Class> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
writeInt(VAL_OBJECTARRAY);
writeArray((Object[]) v);
} else if (v instanceof Serializable) {
// Must be last
writeInt(VAL_SERIALIZABLE);
writeSerializable((Serializable) v);//2
} else {
throw new RuntimeException("Parcel: unable to marshal value " + v);
}
}
}
如果是Parcelable类型,
public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
if (p == null) {
writeString(null);
return;
}
writeParcelableCreator(p);
p.writeToParcel(this, parcelableFlags);
}
首先会写一下Parcelable 对象的类名字,然后调用了Parcelable 对象的writeToParcel。也就是自己实现的方法,就会把我们想要传递的数据写到Parcel 里面去。
在2处,如果调用的是Serializable类型。
public final void writeSerializable(@Nullable Serializable s) {
if (s == null) {
writeString(null);
return;
}
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) {
}
}
会调用ObjectOutputStream将对象进行写出,然后调用writeByteArray,最终调用的native方法进行写入。
拿数据的时候会调用getParcelableExtra(),
public @Nullable T getParcelableExtra(String name) {
return mExtras == null ? null : mExtras.getParcelable(name);
}
public T getParcelable(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (T) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Parcelable", e);
return null;
}
}
这里可以知道直接根据key去获得,而我们知道写入到内存中的时候需要调用writeToParcel,此时需要拿取的时候则需要调用readFromParcel.
public void readFromParcel(Parcel parcel) {
super.readFromParcelInner(parcel);
mFlags = FLAG_ALLOW_FDS;
maybePrefillHasFds();
}
void readFromParcelInner(Parcel parcel) {
// Keep implementation in sync with readFromParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
int length = parcel.readInt();
readFromParcelInner(parcel, length);
}
一连续的调用最后调用的是readFromParcelInner()。
private void readFromParcelInner(Parcel parcel, int length) {
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);
} else if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
mParcelledByNative = false;
return;
} else if (length % 4 != 0) {
throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
}
final int magic = parcel.readInt();
final boolean isJavaBundle = magic == BUNDLE_MAGIC;
final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
if (!isJavaBundle && !isNativeBundle) {
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
if (parcel.hasReadWriteHelper()) {
// If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
// unparcel right away.
synchronized (this) {
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
}
return;
}
// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
p.adoptClassCookies(parcel);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
mParcelledData = p;//3
mParcelledByNative = isNativeBundle;//4
}
看3处,将mParcelledData进行赋值,在看4处,经过断点可知,isNativeBundle为false,即mParcelledByNative = false。然后回到getParcelable()。
public T getParcelable(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (T) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Parcelable", e);
return null;
}
}
先调用unparcel()。
void unparcel() {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
}
}
}
}
从上面知道,mParcelledData不等于null.所以会走initializeFromParcelLocked()。
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
boolean parcelledByNative) {
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ "clobber all data inside!", new Throwable());
}
if (isEmptyParcel(parcelledData)) {
if (mMap == null) {
mMap = new ArrayMap<>(1);
} else {
mMap.erase();
}
mParcelledData = null;
mParcelledByNative = false;
return;
}
final int count = parcelledData.readInt();
ArrayMap map = mMap;
if (map == null) {
map = new ArrayMap<>(count);
} else {
map.erase();
map.ensureCapacity(count);
}
try {
if (parcelledByNative) {
parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);//6
} else {
parcelledData.readArrayMapInternal(map, count, mClassLoader);//7
}
} catch (BadParcelableException e) {
} finally {
mMap = map;
if (recycleParcel) {
recycleParcel(parcelledData);
}
mParcelledData = null;
mParcelledByNative = false;
}
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}
前面知道mParcelledByNative为false,在这里面会走7处。
void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
@Nullable ClassLoader loader) {
int startPos;
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
String key = readString();
Object value = readValue(loader);
outVal.append(key, value);
N--;
}
outVal.validate();
}
调用了readValue。
public final Object readValue(@Nullable ClassLoader loader) {
int type = readInt();
switch (type) {
。。。。
case VAL_PARCELABLE:
return readParcelable(loader);
。。。。
}
}
调用readParcelable().
public final T readParcelable(@Nullable ClassLoader loader) {
Parcelable.Creator> creator = readParcelableCreator(loader);//8
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator>) {
Parcelable.ClassLoaderCreator> classLoaderCreator =
(Parcelable.ClassLoaderCreator>) creator;
return (T) classLoaderCreator.createFromParcel(this, loader);//9
}
return (T) creator.createFromParcel(this);
}
在8处调用readParcelableCreator去获取Parcelable.Creator实例。在改方法中,先根据ClassLoader去缓存中获取,如果有,则直接返回,没有就采用反射的方式获取。
得到Parcelable.Creator实例,然后调用createFromParcel(),而这个方法在我们的bean必须实现。如果没有实现则会报异常。
Android里面为什么要设计出Bundle而不是直接用Map结构
前面我们提出一个问题,既然Bundle里面调用的也是ArrapMap,为什么不直接调用Hashmap,而重新创建一个Bundle。
1、Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的EntryArray比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。
2、另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。
Android中Intent/Bundle的通信原理及大小限制
对于这一块牵扯到binder,自己的功力不够,需要学习的可以看
https://www.jianshu.com/p/ea4fc6aefaa8
这篇文章。写的很详细。
引用文献
https://www.jianshu.com/p/ea4fc6aefaa8