[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零碎問題
- 其他零碎問題
- 開發思路