[TOC]
Android中对于对象有两种方式可以实现序列化,分别为实现Serializable接口和实现Parcelable接口。
# Serializable接口
使用Serializable接口实现序列化时,只要让类实现Serializable接口并声明一个serialVersionUID即可。
```java
public class Book implements Serializable {
private static final long serialVersionUID = 5474576373476457684L;
private String mName;
private float mPrice;
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public float getPrice() {
return mPrice;
}
public void setPrice(float price) {
mPrice = price;
}
}
```
# Parcelable接口
Parcelable接口声明如下:
```java
public interface Parcelable {
public @ContentsFlags int describeContents();
// 对象写入Parcel
public void writeToParcel(Parcel dest, @WriteFlags int flags);
public interface Creator<T> {
// 从Parcel中恢复对象
public T createFromParcel(Parcel source);
// 创建一个对象数组
public T[] newArray(int size);
}
// 支持传入ClassLoader
public interface ClassLoaderCreator<T> extends Creator<T> {
public T createFromParcel(Parcel source, ClassLoader loader);
}
}
```
实现类示例如下:
```java
public class Book implements Parcelable {
private String mName;
private String mAuthor;
private float mPrice;
protected Book(Parcel in) {
mName = in.readString();
mAuthor = in.readString();
mPrice = in.readFloat();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
dest.writeString(mAuthor);
dest.writeFloat(mPrice);
}
}
```
需要注意的一点是,写入对象到Parcel和从Parcel读取对象两个方法中,成员变量的顺序需要一致,否则将会产生异常。
# 对比
在实际开发中,推荐使用Parcelable接口:
1、Serializable使用简单但是开销大,序列化和反序列化过程需要大量的IO操作,使用了反射机制,过程相对缓慢,且产生很多临时对象需要频繁垃圾回收
2、Parcelable使用稍微麻烦,但是效率高
3、Parcelable推荐用在内存序列化上;Serializable推荐用在将对象序列化到存储设备中或对象序列化后通过网络传输。
# Parcelable源码分析
## 序列化
我们使用Intent传递对象是最常见的使用Parcelable序列化使用场景,下面我们来看看序列化后的对象到底是怎么传递的。
```java
public @NonNull Intent putExtra(String name, Parcelable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putParcelable(name, value);
return this;
}
```
上面是Intent的putExtra方法,我们在传递数据时将Parcelable序列化后的对象作为参数传入。可以看到Parcelable对象被存放到mExtras,也就是一个Bundle对象中。看看Bundle的putParcelable方法:
```java
ArrayMap<String, Object> mMap = null;
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
//...
mMap.put(key, value);
}
```
可以看到,序列化后的对象被直接存到了Bundle的一个Map集合中了。由于Bundle也实现了Parcelable接口,在传递数据时,Bundle也是会被传递过去的,会调用其writeToParcel方法:
```java
// 将Bundle中的内容写入到Parcel对象中
public void writeToParcel(Parcel parcel, int flags) {
//...
super.writeToParcelInner(parcel, flags);
}
void writeToParcelInner(Parcel parcel, int flags) {
//...
// 此处直接将Bundle的整个mMap传入
parcel.writeArrayMapInternal(map);
}
```
来看看Parcel对象拿到Map后的操作:
```java
void writeArrayMapInternal(ArrayMap<String, Object> val) {
//...
// 将map中的每一个对象依次写入Parcel中
for (int i=0; i<N; i++) {
writeString(val.keyAt(i));
writeValue(val.valueAt(i));
}
}
public final void writeValue(Object v) {
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 Parcelable) {
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
}
//...
}
public final void writeParcelable(Parcelable p, int parcelableFlags) {
//...
writeParcelableCreator(p);
//调用序列化后的Parcelable对象的writeToParcel方法
p.writeToParcel(this, parcelableFlags);
}
```
可以看到,最终调用了我们实现序列化对象的writeToParcel方法。
## 反序列化
接下来看看从Parcel中恢复Bundle对象:
```java
public void readFromParcel(Parcel parcel) {
super.readFromParcelInner(parcel);
mFlags = FLAG_ALLOW_FDS;
maybePrefillHasFds();
}
void readFromParcelInner(Parcel parcel) {
int length = parcel.readInt();
readFromParcelInner(parcel, length);
}
```
接下来看看readFromParcelInner方法:
```java
private void readFromParcelInner(Parcel parcel, int length) {
//...
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
p.adoptClassCookies(parcel);
p.setDataPosition(0);
mParcelledData = p;
}
```
可以看到,为mParcelledData进行赋值。然后这条线就断了,那么我们就从Bundle中获取传递过来的Parcelable对象看看:
```java
public <T extends Parcelable> T getParcelable(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
//...
return (T) o;
}
```
首先调用unparcel方法,然后根据key从map中获取Parcelable对象。来看看unparcel方法:
```java
void unparcel() {
final Parcel source = mParcelledData;
if (source != null) {
// 这个source就是我们前面拿到的Parcel对象
initializeFromParcelLocked(source, /*recycleParcel=*/ true);
}
//...
}
```
看看initializeFromParcelLocked方法:
```java
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {
ArrayMap<String, Object> map = mMap;
//...
try {
parcelledData.readArrayMapInternal(map, count, mClassLoader);
} catch (BadParcelableException e) {
//...
} finally {
mMap = map;
if (recycleParcel) {
recycleParcel(parcelledData);
}
mParcelledData = null;
}
}
```
最终调用了parcelledData的readArrayMapInternal方法:
```java
void readArrayMapInternal(ArrayMap outVal, int N, ClassLoader loader) {
int startPos;
// 依次取出每一个属性,并存入map
while (N > 0) {
String key = readString();
Object value = readValue(loader);
outVal.append(key, value);
N--;
}
outVal.validate();
}
```
读取属性的readValue方法源码如下:
```java
public final Object readValue(ClassLoader loader) {
int type = readInt();
switch (type) {
case VAL_STRING:
return readString();
case VAL_INTEGER:
return readInt();
case VAL_PARCELABLE:
return readParcelable(loader);
//...
}
}
public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
// 获取Creator
Parcelable.Creator<?> creator = readParcelableCreator(loader);
//...
return (T) creator.createFromParcel(this);
}
```
最终调用Creator的createFromParcel方法,也就是我们自己序列化对象写的那个createFromParcel方法。其中获取Creator代码如下:
```java
public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
String name = readString();
Parcelable.Creator<?> creator;
synchronized (mCreators) {
HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
if (map == null) {
map = new HashMap<>();
mCreators.put(loader, map);
}
creator = map.get(name);
if (creator == null) {
try {
// If loader == null, explicitly emulate Class.forName(String) "caller
// classloader" behavior.
ClassLoader parcelableClassLoader =
(loader == null ? getClass().getClassLoader() : loader);
// Avoid initializing the Parcelable class until we know it implements
// Parcelable and has the necessary CREATOR field. http://b/1171613.
Class<?> parcelableClass = Class.forName(name, false /* initialize */,
parcelableClassLoader);
if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
throw new BadParcelableException("Parcelable protocol requires that the "
+ "class implements Parcelable");
}
Field f = parcelableClass.getField("CREATOR");
if ((f.getModifiers() & Modifier.STATIC) == 0) {
throw new BadParcelableException("Parcelable protocol requires "
+ "the CREATOR object to be static on class " + name);
}
Class<?> creatorType = f.getType();
if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
// Fail before calling Field.get(), not after, to avoid initializing
// parcelableClass unnecessarily.
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
+ "CREATOR on class " + name);
}
creator = (Parcelable.Creator<?>) f.get(null);
}
map.put(name, creator);
}
}
return creator;
}
```
可以看到,通过反射拿到Creator。
- 导读
- Java知识
- Java基本程序设计结构
- 【基础知识】Java基础
- 【源码分析】Okio
- 【源码分析】深入理解i++和++i
- 【专题分析】JVM与GC
- 【面试清单】Java基本程序设计结构
- 对象与类
- 【基础知识】对象与类
- 【专题分析】Java类加载过程
- 【面试清单】对象与类
- 泛型
- 【基础知识】泛型
- 【面试清单】泛型
- 集合
- 【基础知识】集合
- 【源码分析】SparseArray
- 【面试清单】集合
- 多线程
- 【基础知识】多线程
- 【源码分析】ThreadPoolExecutor源码分析
- 【专题分析】volatile关键字
- 【面试清单】多线程
- Java新特性
- 【专题分析】Lambda表达式
- 【专题分析】注解
- 【面试清单】Java新特性
- Effective Java笔记
- Android知识
- Activity
- 【基础知识】Activity
- 【专题分析】运行时权限
- 【专题分析】使用Intent打开三方应用
- 【源码分析】Activity的工作过程
- 【面试清单】Activity
- 架构组件
- 【专题分析】MVC、MVP与MVVM
- 【专题分析】数据绑定
- 【面试清单】架构组件
- 界面
- 【专题分析】自定义View
- 【专题分析】ImageView的ScaleType属性
- 【专题分析】ConstraintLayout 使用
- 【专题分析】搞懂点九图
- 【专题分析】Adapter
- 【源码分析】LayoutInflater
- 【源码分析】ViewStub
- 【源码分析】View三大流程
- 【源码分析】触摸事件分发机制
- 【源码分析】按键事件分发机制
- 【源码分析】Android窗口机制
- 【面试清单】界面
- 动画和过渡
- 【基础知识】动画和过渡
- 【面试清单】动画和过渡
- 图片和图形
- 【专题分析】图片加载
- 【面试清单】图片和图形
- 后台任务
- 应用数据和文件
- 基于网络的内容
- 多线程与多进程
- 【基础知识】多线程与多进程
- 【源码分析】Handler
- 【源码分析】AsyncTask
- 【专题分析】Service
- 【源码分析】Parcelable
- 【专题分析】Binder
- 【源码分析】Messenger
- 【面试清单】多线程与多进程
- 应用优化
- 【专题分析】布局优化
- 【专题分析】绘制优化
- 【专题分析】内存优化
- 【专题分析】启动优化
- 【专题分析】电池优化
- 【专题分析】包大小优化
- 【面试清单】应用优化
- Android新特性
- 【专题分析】状态栏、ActionBar和导航栏
- 【专题分析】应用图标、通知栏适配
- 【专题分析】Android新版本重要变更
- 【专题分析】唯一标识符的最佳做法
- 开源库源码分析
- 【源码分析】BaseRecyclerViewAdapterHelper
- 【源码分析】ButterKnife
- 【源码分析】Dagger2
- 【源码分析】EventBus3(一)
- 【源码分析】EventBus3(二)
- 【源码分析】Glide
- 【源码分析】OkHttp
- 【源码分析】Retrofit
- 其他知识
- Flutter
- 原生开发与跨平台开发
- 整体归纳
- 状态及状态管理
- 零碎知识点
- 添加Flutter到现有应用
- Git知识
- Git命令
- .gitignore文件
- 设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- RxJava
- 基础
- Linux知识
- 环境变量
- Linux命令
- ADB命令
- 算法
- 常见数据结构及实现
- 数组
- 排序算法
- 链表
- 二叉树
- 栈和队列
- 算法时间复杂度
- 常见算法思想
- 其他技术
- 正则表达式
- 编码格式
- HTTP与HTTPS
- 【面试清单】其他知识
- 开发归纳
- Android零碎问题
- 其他零碎问题
- 开发思路