作為Android開發者,我們經常需要在不同的組件(Activity、Service等)之間傳輸數據。這里的"傳輸"往往不僅僅是簡單的數據復制,還可能涉及跨進程的內存復制操作。當傳輸的數據量較大時,這種操作可能會帶來嚴重的性能問題。而Android系統為我們提供了Parcelable這一高效的序列化傳輸機制,很好地解決了這一痛點。今天,就讓我們一起來探訕Parcelable的神奇之處。
一、Parcelable架構與原理
Parcelable是Android中一種高效的序列化機制,用于實現進程間通信(IPC)中的對象傳遞。
Parcelable相對于Serializable的使用相對復雜一些,但Parcelable的效率相對Serializable也高很多,這一直是Google工程師引以為傲的,Parcelable和Serializable的效率對比Parcelable vs Serializable號稱快10倍的效率。
與Serializable接口不同,Parcelable采用的是手工編碼的方式,序列化后的數據更為緊湊。系統將數據打包到一個全局內存區域中,可供不同線程/進程共享訪問。
1、Parcelable的設計理念
Parcelable的設計理念是在保證一定性能的前提下,盡可能節省內存和CPU開銷。
從架構上來看,Parcelable涉及到了Binder驅動、Parcel容器和IPCThreadState等幾個關鍵組件,共同構成了高效的序列化通道:
(1)、Binder驅動
Binder驅動是Android的核心組件之一,負責進程間的數據傳輸。它在內核層為每個進程維護了一塊受保護的共享內存區域,用于在進程間傳遞Parcelable對象。
(2)、Parcel容器
Parcel對象是存儲序列化數據的臨時載體。開發者需要先將對象寫入Parcel中,然后由Binder驅動完成Parcel在進程間的拷貝和傳遞。
(3)、IPCThreadState
IPCThreadState是一個線程私有數據結構,負責在進程間管理請求和應答的Parcel對象數據。每個線程在與其他進程通信時,都會使用自己的IPCThreadState實例。
(4)、Parcelable接口
Parcelable接口定義了將對象寫入和從Parcel容器讀取的抽象協議,開發者需要手動實現這兩個序列化方法。系統會按此協議完成對象的編碼/解碼操作。
序列化的基本流程如下:
- 當一個進程需要向另一個進程傳輸數據時,會先初始化一個Parcel容器對象;
- 將要傳遞的Parcelable對象通過writeToParcel()方法寫入Parcel容器;
- Binder驅動從發送方進程拷貝這個Parcel容器到內核共享內存區域;
- 接收方進程從共享內存區讀取Parcel數據,并通過Parcelable.Creator反序列化出原始對象;
- 接收進程的目標組件(如Activity)即可使用這個反序列化出的對象數據。
整個過程無需經過Java層的序列化操作,因此效率極高。Parcel容器采用面向流的編碼格式存儲數據,格式緊湊,內存占用小。
此外,Parcelable的實現細節還包括:
- 支持平臺默認Java數據類型的高效編解碼;
- 使用標志位壓縮編碼,節省空間;
- 引入Parcel窗口緩存,加快讀寫效率;
- 等等一系列優化手段。
從架構和流程上看,Parcelable不僅擁有簡單的接口定義,而且在系統層得到了全方位的優化支持,使其在Android世界中成為高效低耗的序列化標準。當然,與之對應的是開發者必須自行編寫序列化方法的工作量。但從性能的角度來看,這一點工作量是完全值得的。
二、Parcelable接口的使用
要使用Parcelable,需要自己實現這兩個接口方法。
// 定義一個數據類MyData,實現Parcelable接口
public class MyData implements Parcelable {private int id;private String name;private boolean isAdult;// 構造函數public MyData(int id, String name, boolean isAdult) {this.id = id;this.name = name;this.isAdult = isAdult;}// 從Parcel反序列化時使用的特殊構造函數protected MyData(Parcel in) {id = in.readInt();name = in.readString();isAdult = in.readByte() != 0;}// writeToParcel方法,將數據寫入Parcel@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeInt(id);dest.writeString(name);dest.writeByte((byte) (isAdult ? 1 : 0));}// 生成用于反序列的CREATOR對象public static final Creator<MyData> CREATOR = new Creator<MyData>() {@Overridepublic MyData createFromParcel(Parcel in) {return new MyData(in);}@Overridepublic MyData[] newArray(int size) {return new MyData[size];}};// describeContents是一個內部接口標志@Overridepublic int describeContents() {return 0;}// getter/setter ...
}
接著就可以通過Intent、Binder等方式傳輸這個Parcelable對象了。系統在底層會自動完成序列化和反序列化的工作。
// 使用示例:傳遞Parcelable對象
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 創建MyData對象MyData data = new MyData(1, "Jack", true);// 使用Intent傳遞MyDataIntent intent = new Intent(this, SecondActivity.class);intent.putExtra("data", data);startActivity(intent);}
}// 接收Parcelable對象
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);// 從Intent中取出MyData對象MyData data = getIntent().getParcelableExtra("data");if (data != null) {int id = data.getId();String name = data.getName();boolean isAdult = data.isAdult();// 處理data...}}
}
在這個例子中:
- 我們定義了一個MyData類,實現了Parcelable接口。
- 在MyData類中,我們提供了一個用于Parcel反序列化的特殊構造函數,以及writeToParcel和describeContents方法。
- 同時生成了一個CREATOR對象,用于從Parcel重構MyData實例。
- 在MainActivity中,我們創建了一個MyData對象,并通過Intent將它傳遞給SecondActivity。
- 在SecondActivity中,我們從Intent中取出了MyData對象,可以使用其中的數據。
通過這個案例,你可以看到使用Parcelable傳遞一個自定義數據對象是非常簡單的。只需要完成幾個基本方法的實現,就可以實現對象的高效序列化和反序列化。
與手動實現序列化和Binder傳遞相比,使用Parcelable的代碼更加簡潔、安全,并得到了系統級的性能優化。這就是Parcelable作為Android高效IPC解決方案的魅力所在。
三、Parcelable的使用場景
以下是Parcelable
主要用于的數據傳輸場景,以及結合案例代碼演示和使用Parcelable
的優點分析。
1、Activity間傳遞數據
當從一個Activity
導航到另一個Activity
時,可以使用Intent
攜帶數據。如果數據對象實現了Parcelable
接口,可以直接在Intent
中使用。
// 創建Parcelable對象
MyData myData = new MyData("Hello", 123);// 通過Intent傳遞數據
Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
intent.putExtra("MY_DATA_KEY", myData);
startActivity(intent);
在接收的Activity
中:
// 接收Parcelable數據
Intent intent = getIntent();
MyData myData = intent.getParcelableExtra("MY_DATA_KEY");
2、Activity與Service傳遞數據
Parcelable
也可以用于Activity
和Service
之間的數據傳輸。可以通過Intent
發送數據到Service
,或者Service
返回結果給Activity
。
// Activity發送數據到Service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("MY_DATA_KEY", myData);
startService(serviceIntent);
3、通過Binder傳輸數據
在使用AIDL
(Android Interface Definition Language)定義服務時,Parcelable
可以用來在客戶端和服務器之間傳遞數據。
// 在AIDL接口定義中
parcelable MyData;
4、將對象保存在Bundle或保存實例狀態
在Activity
的生命周期中,可以在onSaveInstanceState
方法中使用Bundle
保存Parcelable
對象。
@Override
public void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);outState.putParcelable("MY_DATA_KEY", myData);
}@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);myData = savedInstanceState.getParcelable("MY_DATA_KEY");
}
四、Parcelable與Serializable的比較
與Java中的Serializable相比,Parcelable有以下優勢:
1、性能更優
Parcelable序列化后的碼流要比Serializable小得多,內存開銷和CPU損耗也更低。
2、使用成本低
Parcelable無需使用反射,只需手寫幾個方法即可。
3、沒有安全隱患
Parcelable不會自動完成數據的深復制,避免了Serializable可能帶來的安全隱患。
當然,其缺點是需要手動編碼實現序列化邏輯,并維護代碼與類結構的同步,工作量較高。而Serializable則可以自動完成序列化。
五、Parcelable的性能優化建議
盡管Parcelable已經相當高效,但我們在實際使用時仍可以通過一些優化手段達到更佳的性能表現:
1、盡量使用標量類型
標量類型(int/long)可以直接通過writeInt/writeLong方法進行序列化,性能較高。
2、減少自動裝箱操作
避免對裝箱對象進行序列化,如Integer等。應直接使用基本類型。
3、編寫高效的Parcelable方法
在writeToParcel方法中應先寫入有效數據,而不是創建臨時對象。
4、啟用Parcelable代碼生成器
Parceler等工具可以自動生成Parcelable代碼,提高開發效率。
六、不得不提及的Bundler
Parcelable的強大遠不止于上述簡單用法。在Android 10開始,Google引入了Bundler框架,可以將任意的應用程序數據自動打包成一個Parcelable的Bundle,從而實現高效的跨進程通信。Bundler極大地簡化了使用Parcelable的難度。
這一強大功能曾廣受期待。令人惋惜的是,Bundler目前的可用性和成熟度似乎還有待提高。但不可否認,Google為我們展現了Parcelable在未來更大的應用前景。
無論是Android系統的Binder,還是Chrome瀏覽器的IPC數據傳輸,Parcelable都扮演著舉足輕重的角色。它使得Android應用能夠高效、安全地在進程間傳輸數據。面向未來,或許Parcelable的序列化能力會不斷增強,甚至取代JVM的Serializable成為跨平臺的數據序列化標準。讓我們拭目以待吧!