Android IPC 基礎(chǔ)概念
多進程應(yīng)用場景
- 應(yīng)用自身需要采用多進程模式來實現(xiàn)含滴。
- 有些模塊需要運行在單獨的線程中俊柔。
- 需要加大一個應(yīng)用可使用的內(nèi)存坎怪,通過多進程來獲取多份內(nèi)存空間敦迄。
//獲取應(yīng)用限制的內(nèi)存大小和進程限制的內(nèi)存大小
//我設(shè)備測試的是 heapGrowthLimit: 256m 斧散,heapSize: 512m
ActivityManager activityManager =(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int heapGrowthLimit = activityManager.getMemoryClass();// 單個應(yīng)用可用最大內(nèi)存
int heapSize = activityManager.getLargeMemoryClass();//單個進程可用的最大內(nèi)存
Log.d(TAG, "heapGrowthLimit: " + heapGrowthLimit + "\nheapSize: " + heapSize);
- 需要向其他應(yīng)用獲取數(shù)據(jù)玻驻,也就是跨進程訪問數(shù)據(jù)悼凑。
如何跑在同一進程
- 兩個應(yīng)用能跑在同一進程的前提條件是具有相同的ShareUID和簽名。
- 具有相同ShareUID并且簽名相同的兩個應(yīng)用璧瞬,可以共享對方私有數(shù)據(jù)户辫,比如data目錄、組件信息等嗤锉。
- 如果ShareUID相等渔欢、簽名相同且跑在同一進程中,那么除了能共享data目錄瘟忱、組件信息等奥额,還能共享內(nèi)存數(shù)據(jù)。
開啟多進程的方式
使一個應(yīng)用開啟多進程模式访诱,有以下兩種方式:
- AndroidMenifest中指定android:process屬性垫挨。
- 通過JNI在native層去fork一個新進程。
第二種不常用触菜,暫時只看第一種九榔。AndroidMenifest中沒有給某個組件指定process屬性時默認(rèn)運行默認(rèn)進程,默認(rèn)進程的名字為程序包名涡相。如果要指定哲泊,有兩種方式。
android:process=":jtt"
android:process="com.utte.test.jtt"
兩種方式還是有區(qū)別的:
- 第一種分號的意思是進程名前面加上當(dāng)前包名催蝗,進程名為"com.utte.test:jtt"切威。
- 第二種的進程名直接就是屬性值,進程名為"com.utte.test.jtt"生逸。
- 第一種屬于當(dāng)前應(yīng)用的私有進程牢屋,其他應(yīng)用的組件不能與其跑在同一進程中。
- 第二種為全局進程槽袄,其他應(yīng)用可通過ShareUID的方式與其跑在同一進程中。
多進程運行機制
Android為每個應(yīng)用也就是說為每個進程都分配了一個獨立的虛擬機锋谐,不同的虛擬機在內(nèi)存分配上有不同的地址空間遍尺,就導(dǎo)致在不同的虛擬機中訪問同一個類的對象時會產(chǎn)生多份副本。
所有運行在不同進程中的四大組件都不能通過共享內(nèi)存來共享數(shù)據(jù)涮拗,所以會造成以下影響:
- 靜態(tài)成員和單例模式完全失效
會分配不同虛擬機乾戏,所以會產(chǎn)生多個對象副本迂苛。
- 線程同步機制完全失效
不同進程中的對象不是同一個對象,所以加鎖達(dá)不到效果鼓择。
- SharePreferences的可靠性降低
SharePreferences不支持兩個進程同時進行寫操作三幻,會導(dǎo)致一定幾率數(shù)據(jù)丟失,SharePreferences底層是通過讀寫XML來實現(xiàn)的呐能,并且內(nèi)存中會有緩存念搬。
- Application會多次創(chuàng)建
當(dāng)一個組件跑在新進程中時,系統(tǒng)會在創(chuàng)建新進程時同時分配獨立的虛擬機令杈,這個過程是一個應(yīng)用啟動的過程锯岖,相當(dāng)于把應(yīng)用重新啟動了一次纫谅,自然Application也會重新創(chuàng)建。
運行在兩個進程中的兩個組件會擁有獨立的虛擬機爷恳、獨立的Application、獨立的內(nèi)存空間象踊。
它們也就相當(dāng)于是兩個應(yīng)用擁有相同的ShareUID和簽名温亲,但不跑在同一進程中,它們可以訪問對方數(shù)據(jù)杯矩,但是不能通過共享內(nèi)存的方式栈虚。雖然不能共享內(nèi)存了,但是還有很多方式可以實現(xiàn)跨進程的數(shù)據(jù)交互菊碟。
多進程通信的主要方式
- Binder
- Bundle
- 文件共享
- AIDL
- Messenger
- ContentProvider
- Socket
Bundle节芥、文件共享、Socket比較簡單逆害,也基本都使用過头镊,其他的都與Binder有關(guān),包括ContentProvider魄幕,它的底層其實也是Binder相艇。
序列化
我們傳輸?shù)臄?shù)據(jù)必須能夠被序列化,比如基本類型纯陨、實現(xiàn)了Parcelable接口的對象坛芽、實現(xiàn)Serializable接口的對象以及Android支持的一些特殊對象。
Serializable接口
通過Serializable使一個對象實現(xiàn)序列化:
- 類實現(xiàn)Serializable接口
- 聲明serialVersionUID (非必需)
類定義:
public class Book implements Serializable {
private static final long serialVersionUID = 199898L;
\\...
}
序列化使用ObjectOutputStream.writeObject()翼抠,反序列化使用ObjectInputStream.read()咙轩。
序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同才可以被正常的反序列化。如果不手動指定serialVersionUID阴颖,系統(tǒng)就會根據(jù)類信息計算hash值賦值給serialVersionUID活喊。這樣的話如果在序列化后對類進行了一些更改,在沒有指定serialVersionUID的情況下量愧,會反序列化失敗報異常钾菊,如果指定了帅矗,在非毀滅性改變的前提下,程序會盡可能最大限度的恢復(fù)數(shù)據(jù)煞烫。
Parcelable接口
更詳細(xì)的內(nèi)容可以看這篇博客浑此,Android中Serializable和Parcelable序列化對象詳解。
Parcelable的用法如下滞详,需要實現(xiàn)Parcelable序列化凛俱、反序列化、描述三個邏輯茵宪。
public class User implements Parcelable {
private boolean isMale;
private Book mBook; //Serializable對象
private Bag mBag; //Parcelable對象
public User(boolean isMale, Book book, Bag bag) {
this.isMale = isMale;
mBook = book;
mBag = bag;
}
//描述
@Override
public int describeContents() {
return 0;
}
//序列化
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(isMale ? 1 : 0);
out.writeSerializable(mBook);
out.writeParcelable(mBag, 0);
}
//反序列化
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
//給反序列化創(chuàng)建對象調(diào)用
private User(Parcel in) {
isMale = in.readInt() == 1;
mBook = (Book) in.readSerializable();
mBag = in.readParcelable(Bag.class.getClassLoader());
}
}
- Parcel為內(nèi)部包裝了可序列化的數(shù)據(jù)最冰。
- writeToParcel()的flag如果為1就表示當(dāng)前對象需要作為返回值返回,不能立即釋放稀火,0表示不需要暖哨,幾乎所有情況都是0。
- describeContents()返回0表示不含文件描述符凰狞,存在文件描述符時返回1篇裁,幾乎所有時候都應(yīng)該返回0。
獲取到文件描述符可以完成所有文件相關(guān)的操作赡若,因為作用大达布,所以為了防止泄露,需要禁止在Bundle傳輸Parcel時包含文件描述符逾冬,如果通過Parcel中包含ParcelFileDescriptor的Bundle使用時就會拋出IllegalArgumentException黍聂。這個值是在系統(tǒng)內(nèi)部進行安全保護所使用的,其他情況下填0即可身腻。
Serializable和Parcelable比較
Serializable | Parcelable |
---|---|
使用比較簡單 | 使用稍微麻煩 |
IO流形操作产还,性能較低 | 基于內(nèi)存操作,效率高 |
Parcelable主要用于內(nèi)存序列化嘀趟。Serializable主要用于序列化到存儲設(shè)備或網(wǎng)絡(luò)傳輸脐区。