进程
进程有自己的内存地址, 一个进程中的1000地址可能在另一个进程中是10000, java的引用本质上还是内存地址, 如果要传递一个类的实例, 还需要传递方法等等, 方法是独立与类对象存在的, 所以到另一个进程中去引用同一个方法就错了, 还是因为独立内存地址的原因.
Android中Activity之间并不能保证两个Activity在同一个进程中, 比如一个APP调用系统打电话功能, 就是两个进程, 所以需要进程间通信
序列化
- 序列化一个实例对象编码成字节流, 存入物理内存/用于传输
- 反序列化从字节流对象中再次重新构建对象实例。
和JSON XML不同的是 JSON XML 是字符描述型对象, 它们是通用的, 不依赖于任何语言平台
SerializableLink
JVM 虚拟机中的对象, 其内部的状态只存在于内存中, JVM 停止后这些数据就丢失了, 所以考虑到持久化 , 通常是保存在文件系统或者数据库中, 比如 对象映射关系(Object-relational mapping), 对象序列化机制(Object serialization)是Java提供的一种对象持久化方式, 将JVM中的对象和字节数组流之间进行转换
过程简述: Java 的 ObjectOutputStream 类用来持久化一个对象, 通过 writeObject 方法把这个类的对象写到一个文件, 再通过 ObjectInputStream 的readObject 方法把这个对象读出来.
- 具备 serialVersionUID, 用来标识当前序列化对象的版本, 如果需要本地存储, 建议每一个实现 Serializable 的类都指定 serialVersionUID. 如果没有指定, JVM 会根据类的信息自动生成一个 UID, 我们可以通过 JDK 的 serialver 命令来查看一个 .class 的 UID
- 被
transient
描述的域和类的静态变量不会被序列化 (static修饰的变量会改变, 但那是因为它放在静态区, 而不是因为序列化)Link - transient 只能描述变量, 不能描述类和方法(局部变量无法被修饰--类的方法中定义的变量)
- 如果一个实现了Serializable的类继承自另一个类, 那么这个类必须实现Serializable或者提供一个无参构造函数
- 反序列化并不是通过构造器创建的,
- 因为序列化的过程是可见的, 所以EffectiveJava的作者在第77节中希望使用静态内部类防止被攻击
过程
- 序列化
- 是否替换即将写入流的对象, writeReplace, 比如使用静态内部类代理
- 将对象写成流, writeObject,
- ObjectOutputStream.defaultWriteObject()默认的序列化过程
- 反序列化
- 将流读成对象, readObject
- 如果序列化时自定义了序列化过程, 这里也需要自定义反序列化过程
- 是否替换从流中读出来的对象, readResolve
- 将流读成对象, readObject
- 示例bean
import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; class Persion implements Serializable { public String desc; public String name; public Persion(String desc, String name) { this.desc = desc; this.name = name; } static class SerializableProxy implements Serializable{ private String desc; private String name; private SerializableProxy(Persion s) { this.desc = s.desc; this.name = s.name; } /** * 在这里恢复外围类 * 注意看这里!!!最大的好处就是我们最后得到的外围类是通过构造器构建的! */ private Object readResolve() { return new Persion(desc,name); } } /** * 外围类直接替换成静态内部代理类作为真正的序列化对象 * @return */ private Object writeReplace() { return new SerializableProxy(this); } /** * 这里主要是为了防止攻击,任何以Persion声明的对象字节流都是流氓!! * 因为我在writeReplace中已经把序列化的实例指向了SerializableProxy * @param stream * @throws InvalidObjectException */ private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("proxy requied!"); } }
查看 serialVersionUID 的方法
-
.dat方法查询 Link不推荐 - serialver 命令 Link
cd 到 java 文件目录, java文件直接javac 类名.class
编译, 并serialver 类名
, android 需要将文件删掉包名, 并没有父类或父类也跟着复制过来
Parcelable
Parcel
是一系列java通过C++调用内存的操作, 将序列化的数据写入共享内存, 实现跨进程通信
- 根据这句宏定义得出,Link
#define PAD_SIZE(s) (((s)+3)&~3
内存存放机制和C++的结构体的内存对齐一样, 即读取最小字节位32bit(4字节), 如果高于4字节, 以实际数据类型存放, 但得为4的倍数
- 占用 32 bit,(<= 32 bit), 例如: boolean, char, int
- 实际占用字节(> 32 bit), 例如: long, float, String, 数组等
由此可以知道, 当我们写入/读取一个数据时, 偏移量至少为4Byte, 偏移量公式
f(x) = 4 * x (x=0, 1, ...)
- writeXXX 和 readXXX 导致的偏移量是共用的, 我们在writeInt(23)后, 此时的dataposition = 4, 读取的时候, 我们需要将偏移量置为0, 再从0开始读取4个字节, 所以需要先
setDataPosition(0)
, 再readInt(). - 如果预分配的空间不够时newSize = ((mDataSize+len) * 3)/2;会一次多分配50%;
- 对于不同数据的存储不一样
- 对于普通数据, 使用的是 mData 内存地址,
- 对于IBinder 或者 FileDescriptor, 使用的是 mObjects 内存地址, 通过 flatten_binder() 和 unflatten_binder()实现, 目的是反序列化时读出的对象就是愿对象而不是 new 出来的新对象
测试BinderData 但是目前已经不能找不到 BinderData 这个类
有两个成员
uint8_t* mData; //用来存储序列化流数据,可以把它理解成共享内存
size_t* mObjects; //用来存储IBinder和FileDescriptor
引用和IBinder的序列化方式不一样
过程
- 序列化
- 基本类型直接 writeString / writeInt 然后调用 nativeWriteString / nativeWriteInt 用C++操作
- 包含的子类用 writeToParcel 把类名还是用 nativeWriteString 传给C++操作
- 反序列化
- CREATOR
- createFromParcel 中从流中new出对象和
return new Pojo(in);
- CREATOR
Serializable 完整传递过程
结论: Intent 传递的 Serializable 数据最后还是由 Parcel 传递给 C++ 操作, 但是 比普通的 Parcel 数据多了调用 Stream 的 I/O 操作
传递是靠这两句
intent.putExtra("myserializabledata", persion);
startActivity(intent);
先看第一句
- Intent.putExtra
public Intent putExtra(String name, Serializable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
//调用了 Bundle.putSerializable
mExtras.putSerializable(name, value);
return this;
}
- -> Bundle.putSerializable
@Override
public void putSerializable(@Nullable String key, @Nullable Serializable value) {
//调用了BaseBundle.putSerializable
super.putSerializable(key, value);
}
- -> BaseBundle.putSerializable
void putSerializable(@Nullable String key, @Nullable Serializable value) {
unparcel();
//数据存在 BaseBundle.mMap
mMap.put(key, value);
}
最后将 Serializable 存在了 BaseBundle 的 mMap.
再看第二句
- startActivity 可以理解为最后由 ActivityManager 执行, 而ActivityManager在源码中各版本不一定一致, 先看4.4.4的
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, String profileFile,
ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
...
if (options != null) {
data.writeInt(1);
//这里调用 Bundle.writeToParcel
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
...
return result;
}
- -> Bundle.writeToParcel
@Override
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
try {
//调用 BaseBundle.writeToParcelInner
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}
- -> BaseBundle.writeToParcelInner(parcel, flags)
void writeToParcelInner(Parcel parcel, int flags) {
// Keep implementation in sync with writeToParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
final Parcel parcelledData;
synchronized (this) {
parcelledData = mParcelledData;
}
if (parcelledData != null) {
if (isEmptyParcel()) {
parcel.writeInt(0);
} else {
int length = parcelledData.dataSize();
parcel.writeInt(length);
parcel.writeInt(BUNDLE_MAGIC);
parcel.appendFrom(parcelledData, 0, length);
}
} else {
// Special case for empty bundles.
if (mMap == null || mMap.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
parcel.writeArrayMapInternal(mMap);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
}
- -> Parcel.writeArrayMapInternal(mMap)
void writeArrayMapInternal(ArrayMap<String, Object> val) {
if (val == null) {
writeInt(-1);
return;
}
// Keep the format of this Parcel in sync with writeToParcelInner() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
final int N = val.size();
writeInt(N);
if (DEBUG_ARRAY_MAP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Writing " + N + " ArrayMap entries", here);
}
int startPos;
for (int i=0; i<N; i++) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
writeString(val.keyAt(i));
//开始操作 map 中的 value
writeValue(val.valueAt(i));
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
+ " " + val.keyAt(i));
}
}
这里操作了第一步生成的 mMAp
- -> 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) {
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must
// come before the Parcelable case, so that their specific VAL_*
// types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
} 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) {
// Must be last
writeInt(VAL_SERIALIZABLE);
//对 Serializable 操作
writeSerializable((Serializable) v);
} else {
throw new RuntimeException("Parcel: unable to marshal value " + v);
}
}
}
在writeValue的最后,处理了 Serializable
- -> Parcel.writeSerializable()
public final void writeSerializable(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();
//将Serializable I/O操作后转换成字节流
writeByteArray(baos.toByteArray());
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException writing serializable object (name = " + name +
")", ioe);
}
}
- -> Parcel.writeByteArray()
public final void writeByteArray(byte[] b) {
//对流判断
writeByteArray(b, 0, (b != null) ? b.length : 0);
}
- -> Parcel.nativeWriteByteArray()
public final void writeByteArray(byte[] b, int offset, int len) {
if (b == null) {
writeInt(-1);
return;
}
Arrays.checkOffsetAndCount(b.length, offset, len);
//调用了 native 方法
nativeWriteByteArray(mNativePtr, b, offset, len);
}
Intent
Intent 的 bundle 使用Binder机制进行数据传递, 能使用Binder的缓冲区有大小限制, 有些手机是2M
一个进程默认有16个 Binder线程, 所以一个线程所能占用的缓冲区更小了(大约一个线程128KB), 所以当出现The Binder transaction failed because it was too large, 说明数据太大.
因此Intent传递List和Bitmap对象是存在风险的
参考