1 Android IPC 簡介
IPC是Inter-Process Communication 的縮寫,意爲進程間通訊或跨進程通信,指兩個進程之間進行數據交換的過程。一個進程可以包含多個線程,最簡單的情況下,一個進程可以只有一個線程,即主線程(Android的UI線程)。
IPC機制並不是Android獨有的,任何一個操作系統都需要有相應的IPC機制,比如Windows上可以通過剪貼板、管道和郵槽等來進行進程間通信;Linux上可以通過命名管道、共享內容、信號量等來進行進行進程間通信。Android中最具特色的通信方式就是Binder,還支持Socket,通過Socket可以實現任意兩個終端之間的通信,同一個設備的兩個進程也可以通過Socket實現。
IPC的使用場景:只有在多進程的時候纔會進行進程間通信
- 一個進程需要多進程模式實現,比如有些模塊由於特殊原因需要運行在單獨的進程中、爲了加大一個應用可使用的內存所以需要通過多線程來獲取多份內存空間。
- 當前應用需要向其他應用獲取數據。
2 Android的多進程模式
Android 通過給四大組件指定android:process屬性,即可以開啓多線程。
2.1 開啓多線程模式
在Android中使用多進程只有一種方法:給四大組件在AndroidManifest中指定android:process屬性,value值就是指定的進程名稱,無法爲一個線程或類等指定其運行時所在的進程。
通過shell命令查看:adb shell ps或者adb shell ps | grep com.gqq.xxx.xxx(包名) 查看運行的進程信息:
注意: android:process值::remote
指在當前進程名前加上當前包名,可以理解爲相對名稱,屬於當前應用的私有進程,其他應用組件不可以和它跑在同一個進程中。不以:
開頭的,是一種完整的命名方式,此進程屬於全局進程,其他應用通過ShareUID的方式可以和它跑在同一個進程中。
Android系統會爲每個應用分配一個唯一的UID,具有相同UID的應用才能共享數據,如果兩個應用通過ShareUID跑在同一個線程中,需要應用有相同的ShareUID並且簽名相同,跑在同一個線程,除了共享data目錄、組件信息,還可以共享內存數據。相當於一個應用的兩部分。
2.2 多進程模式的運行機制
Android 爲每一個應用分配一個獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間,導致在不同的虛擬機上訪問同一個類的對象會產生多個副本。比如不同進程對靜態變量等的修改,不會影響其他進程,那麼運行在不同進程的四大組件通過內存共享數據,就會共享失敗。
總體,多進程會帶來的幾方面的問題:
- 靜態成員和單例模式完全失效
- 線程同步機制完全失效
不在一塊內存,不同進程鎖的不是同一個對象。 - SharePreference的可靠性下降
SharePreference的底層實現時通過讀/寫XML文件來實現的,併發讀/寫都有可能出現問題,所以不支持兩個進程同時執行讀寫操作,否則會導致一定機率的數據丟失。 - Application會多次創建
創建一個進程會創建一個虛擬機,也就是啓動一個應用,自然會創建新的Application。運行在同一個進程中的組件屬於同一個虛擬機和同一個Application以及分配同一塊內存。
通過跨進程通信,可以實現數據交互,實現跨進程通訊的方法:
- Intent傳遞數據
- 共享文件和SharePreference
- 基於Binder的Messager和AIDL
- Socket
3. IPC 基本概念介紹
主要三個概念,幫助更好的理解進程間通信。
- Serializable 接口
- Parcelable 接口
- Binder
Serializable 和 Parcelable接口可以完成對象的序列化,用於通過Intent和Binder傳遞數據,需要把對象持久化到存儲設備上或通過網絡傳輸給其他客戶端,可需要通過Serializable完成對象的持久化。
3.1 Serializable 接口
Serializable是java的一個序列化的空接口,提供標準的序列化和反序列化操作,序列化的類需要實現Serializable接口及聲明serialVersionUID,而serialVersionUID不是必需的,但是會影響反序列化的結果。serialVersionUID是靜態成員變量,不參與序列化過程。反序列化時serialVersionUID與序列化時的serialVersionUID一致,則可以正常序列化。不指定serialVersionUID,如果反序列化時當前類發生了變化,比如增刪了某些成員變量,會重新計算當前類hash值並賦與serialVersionUID,導致與序列化時的serialVersionUID不一樣,會造成反序列化失敗。指定serialVersionUID會在極大程度上避免反序列化的失敗,但是如果類發生了結構性的變化,比如類名和成員變量的類型等,還是會反序列化失敗,因爲無法還原新結構的對象。
// 序列化過程
User user = new User(1, "gqq", false);
ObjectOutputStream out = ObjectOutputStream(new FileOutputStream(file));
out.writeObject();
out.close();
// 反序列化過程
ObjectInputStream in = ObjectInputStream(new FileInputStream(file));
User user = in.readObject();
in.close();
注意:靜態成員變量屬於類不屬於對象,不參與序列化;用transient關鍵字標記的成員變量不參與序列化。
繼承自Serializable的默認序列化過程是可以改變的,可以重寫系統的序列化writeObject()
和反序列化方法readObject()
。
3.2 Parcelable接口
Parcelable 是Android提供的一種序列化接口,接口需要實現序列化、反序列化、內容描述。
public class UserModelParcel implements Parcelable{
private int userId;
private String userName;
private boolean isMale;
private UserParcel userParcel;
// 反序列化方法
protected UserModelParcel(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readByte() != 0;
userParcel = in.readParcelable(UserParcel.class.getClassLoader());
}
public UserModelParcel(int userId, String userName, boolean isMale, UserParcel userParcel) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
this.userParcel = userParcel;
}
// 序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeString(userName);
dest.writeByte((byte) (isMale ? 1 : 0));
dest.writeParcelable(userParcel, flags);
}
// 內容描述
@Override
public int describeContents() {
return 0;
}
// 反序列化
public static final Creator<UserModelParcel> CREATOR = new Creator<UserModelParcel>() {
@Override
public UserModelParcel createFromParcel(Parcel in) {
return new UserModelParcel(in);
}
@Override
public UserModelParcel[] newArray(int size) {
return new UserModelParcel[size];
}
};
總結: Serializable 是java的序列化接口,使用簡單但是開銷比較大,序列化和反序列化都涉及到大量的I/O操作,效率相對較低。Parcelable是Android提供的序列化方法,更適用於Android平臺,效率很高,但是使用起來比較麻煩。Parcelable主要用在內存序列化上,序列化存儲設備或將序列化後的對象通過網絡傳輸建議使用Serializable。
再貼一遍
參考 Demo