注意:本篇文章是本人閱讀相關(guān)文章所寫下的總結(jié),方便以后查閱,所有內(nèi)容非原創(chuàng),侵權(quán)刪祟霍。
本篇文章內(nèi)容來(lái)自于:
Android開(kāi)發(fā)藝術(shù)探索
目錄
- 什么是IPC
- Android中的多進(jìn)程模式
2.1 如何開(kāi)啟多進(jìn)程模式
2.2 多進(jìn)程模式的壞影響(為什么會(huì)有各種跨進(jìn)程通信方式) - IPC基礎(chǔ)概念
3.1 Serializable接口
3.2 Parcelable接口
3.3 Binder接口
1. 什么是IPC
IPC(Inter-Process Communication),進(jìn)程間通信/跨進(jìn)程通信锉屈,指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換苍狰。
進(jìn)程和線程的區(qū)別
線程:線程是CPU調(diào)度的最小單元办龄,同時(shí)線程是一種有限的系統(tǒng)資源。
進(jìn)程:進(jìn)程一般指一個(gè)執(zhí)行單元淋昭,在PC和移動(dòng)設(shè)備上指一個(gè)程序或者一個(gè)應(yīng)用俐填。
一個(gè)進(jìn)程可以包含多個(gè)線程,因此進(jìn)程和線程是包含和被包含的關(guān)系翔忽。
Android的進(jìn)程通信方式
通過(guò)Binder可以實(shí)現(xiàn)進(jìn)程間的通信英融。
通過(guò)Socket可以實(shí)現(xiàn)兩個(gè)終端之間的通信。
使用多進(jìn)程的場(chǎng)景(2種)
場(chǎng)景一:
一個(gè)應(yīng)用因?yàn)槟承┰蜃陨硇枰捎枚噙M(jìn)程模式來(lái)實(shí)現(xiàn)歇式。
比如有些模塊由于特殊原因需要運(yùn)行在單獨(dú)的進(jìn)程中驶悟。
比如為了加大一個(gè)應(yīng)用可使用的內(nèi)存所以需要通過(guò)多進(jìn)程來(lái)獲取多份內(nèi)存空間。Android為單個(gè)應(yīng)用使用的最大內(nèi)存做了限制材失,
場(chǎng)景二:
當(dāng)前應(yīng)用需要向其他應(yīng)用獲取數(shù)據(jù)痕鳍,因?yàn)槭?個(gè)應(yīng)用,則必須采用跨進(jìn)程的方式來(lái)獲取所需的數(shù)據(jù)龙巨;
甚至我們通過(guò)系統(tǒng)提供的ContentProvider去查詢數(shù)據(jù)的時(shí)候笼呆,也是一種進(jìn)程間通信,只不過(guò)通信細(xì)節(jié)被系統(tǒng)內(nèi)部屏蔽了旨别。
2. Android中的多進(jìn)程模式
2.1 如何開(kāi)啟多進(jìn)程模式
多進(jìn)程:一個(gè)應(yīng)用中存在多個(gè)進(jìn)程诗赌。
2.1.1 方法一:
通過(guò)四大組件指定android:process屬性,則可開(kāi)啟多進(jìn)程模式秸弛。
<activity android:name=".DemoActivity"
android:process=":remote"/>
<activity android:name=".HelloActivity"
android:process="com.xl.haha">
- DemoActivity啟動(dòng)時(shí)境肾,系統(tǒng)會(huì)為它創(chuàng)建一個(gè)新的進(jìn)程,進(jìn)程名為"com.xl.demo:remote"胆屿。(假設(shè)當(dāng)前包名為"com.xl.demo")
- HelloActivity啟動(dòng)時(shí)奥喻,進(jìn)程名為"com.xl.haha"
- 沒(méi)有指定process屬性的四大組件,則運(yùn)行在默認(rèn)進(jìn)程非迹,進(jìn)程名為包名
android:process=":remote"與"com.xl.haha"的區(qū)別
區(qū)別一:
":remote"的":"含義是指當(dāng)前的進(jìn)程名前面加上當(dāng)前的包名,是簡(jiǎn)寫环鲤。
"com.xl.haha"是完整的命名方式。
區(qū)別二:
":remote"是當(dāng)前應(yīng)用的私有進(jìn)程憎兽,其他應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程中冷离。
"com.xl.haha"是全局進(jìn)程,其他應(yīng)用通過(guò)shareUID方式可以和它跑在同一個(gè)進(jìn)程中纯命。
Android系統(tǒng)會(huì)為每個(gè)應(yīng)用分配一個(gè)唯一的UID西剥,具有相同UID的應(yīng)用才能共享數(shù)據(jù)。
兩個(gè)應(yīng)用通過(guò)shareUID跑在同一個(gè)進(jìn)程中是有要求的亿汞,需要2個(gè)應(yīng)用有相同的shareUID并且簽名相同才可以瞭空。
此時(shí),他們可以互相訪問(wèn)彼此私有數(shù)據(jù)(data目錄、組件信息等)咆畏,不管他們是否跑在同一個(gè)進(jìn)程中南捂。
2.1.2 方法二:
通過(guò)JNI在native層去fork一個(gè)進(jìn)程。
2.2 多進(jìn)程模式的壞影響
多進(jìn)程帶來(lái)的影響:不同進(jìn)程中的四大組件不可以通過(guò)內(nèi)存來(lái)共享數(shù)據(jù)旧找。
因?yàn)閍ndroid為每個(gè)應(yīng)用/每個(gè)進(jìn)程分配一個(gè)獨(dú)立的虛擬機(jī)溺健,不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間,導(dǎo)致在不同的虛擬機(jī)訪問(wèn)同一個(gè)類的對(duì)象會(huì)產(chǎn)生多個(gè)副本钮蛛。
總而言之
使用多進(jìn)程會(huì)造成如下幾方面的問(wèn)題:
1.靜態(tài)成員和單例模式完全失效
2.線程同步機(jī)制完全失效
3.sharepreferences的可靠性下滑鞭缭。
4.application會(huì)多次重建。
為了解決這個(gè)問(wèn)題魏颓,雖然不能直接地共享內(nèi)存岭辣,但是系統(tǒng)提供很多跨進(jìn)程通信方法來(lái)實(shí)現(xiàn)數(shù)據(jù)交互。
跨進(jìn)程通信方式有:
通過(guò)Intent來(lái)傳遞數(shù)據(jù)
共享文件和sharedPreferences
基于Binder的Messenger和AIDL 單方面的客戶端向服務(wù)端推消息用AIDL
Socket 兩者互通用Socket
3. IPC基礎(chǔ)概念
Serializable和Parcelable可以完成對(duì)象的序列化過(guò)程琼开。
- 當(dāng)我們需要用Intent或者Binder傳輸數(shù)據(jù)時(shí)需要使用Parcelable或Serializable
- 我們需要將對(duì)象持久化到設(shè)備上或者通過(guò)網(wǎng)絡(luò)傳輸時(shí)易结,需要用Serializable完成對(duì)象持久化
3.1 Serializable接口
使用Serializable來(lái)序列化:
只需要這個(gè)類實(shí)現(xiàn)Serializable接口并聲明一個(gè)serialVersionUID(不是必須的)即可
//User類
public class User implements Serializable{
private static final long serialVersionUID = 3983908423L;
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
...
}
//實(shí)現(xiàn)序列化
User user = new User("a", 1);
File dir = Environment.getExternalStorageDirectory();
File file = new File(dir, "haha.txt");
//序列化
try {
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(user);
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
//反序列化
try {
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
User user1 = (User) ois.readObject();
Log.d("xl", user1.toString());
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
關(guān)于serialVersionUID
serialVersionUID用于輔助序列化和反序列化過(guò)程的。
序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同才能夠正常的反序列化柜候。
serialVersionUID的詳細(xì)工作機(jī)制:
序列化的時(shí)候系統(tǒng)會(huì)把當(dāng)前類的serialVersionUID寫入序列化的文件中搞动,當(dāng)反序列化的時(shí)候系統(tǒng)會(huì)去檢測(cè)文件中的serialVersionUID,看它是否和當(dāng)前類的serialVersionUID一致渣刷。
如果一致則說(shuō)明序列化的類的版本和當(dāng)前類的版本是相同的鹦肿,這個(gè)時(shí)候可以成功反序列化;否則則說(shuō)明當(dāng)前類和序列化的類相比發(fā)生了某些變換(比如成員變量的數(shù)量辅柴、類型發(fā)生變化)箩溃,則無(wú)法正常反序列化。
建議手動(dòng)賦值碌嘀,在很大程度上避免反序列化的失敗涣旨。
3.2 Parcelable接口
只需要實(shí)現(xiàn)這個(gè)接口即可。
public class User implements Parcelable{
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
}
//反序列化
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];
}
};
//內(nèi)容描述
@Override
public int describeContents() {
return 0;
}
//序列化
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
}
關(guān)于Parcel
Parcel內(nèi)部包裝了可序列化的數(shù)據(jù)股冗,
選擇Parcelable還是Serializable霹陡?
Serializable是Java中的序列化接口,使用簡(jiǎn)單但開(kāi)銷大止状,序列化和反序列化需要大量IO操作烹棉。
Parcelable是Android中的序列化方式,更適合用于Android怯疤,只是操作麻煩浆洗,但效率高,是Android推薦的序列化方式集峦。
因此首選Parcelable伏社。
Parcelable主要用在內(nèi)存序列化上抠刺。
將對(duì)象序列化到存儲(chǔ)設(shè)備或者網(wǎng)絡(luò)傳輸用Serializable
3.3 Binder接口
Binder是Android的一個(gè)類,實(shí)現(xiàn)了IBinder接口洛口。
public class Binder implements IBinder {
3.3.1 什么是Binder
- 從IPC角度來(lái)說(shuō)矫付,Binder是Android中的一個(gè)跨進(jìn)程通信方式
- 可以理解為一種虛擬的物理設(shè)備凯沪,設(shè)備驅(qū)動(dòng)是/dev/binder第焰,該通信方式linux中沒(méi)有。
- 從Android Framework角度來(lái)說(shuō)妨马,Binder是ServiceManager連接各種Manager(ActivityManager等)和相應(yīng)ManagerService的橋梁挺举。
- 從Android應(yīng)用層來(lái)說(shuō),Binder是客戶端和服務(wù)端進(jìn)行通信的媒介烘跺,當(dāng)bindService時(shí)湘纵,服務(wù)端返回一個(gè)包含服務(wù)端業(yè)務(wù)調(diào)用的Binder對(duì)象,通過(guò)這個(gè)Binder對(duì)象滤淳,客戶端可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)梧喷,這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)。
Binder主要用于Service中脖咐,包括AIDL和Messenger铺敌。
其中普通Service中的Binder不涉及進(jìn)程間通信。
Messenger的底層其實(shí)是AIDL屁擅。
3.3.2 Binder的使用以及工作機(jī)制
ALDL示例(Binder的使用):
Android:如何創(chuàng)建一個(gè)AIDL
完成創(chuàng)建后偿凭,SDK會(huì)自動(dòng)生成AIDL所對(duì)應(yīng)的Binder類。
在build/generated/source/aidl/你的 flavor/ 下派歌。
Binder的工作機(jī)制
使用時(shí)弯囊,當(dāng)調(diào)用相應(yīng)的接口方法getBookList(),
則會(huì)先調(diào)用Stub的getBookList()方法胶果,
該方法首先創(chuàng)建該方法的輸入型和輸出型Parcel對(duì)象_data匾嘱、_reply 和返回值對(duì)象,
將方法參數(shù)寫入_data早抠,再調(diào)用transact方法來(lái)發(fā)起RPC請(qǐng)求霎烙,同時(shí)當(dāng)前線程掛起。
然后服務(wù)端的onTransact方法會(huì)被調(diào)用贝或。
直到RPC過(guò)程返回后吼过,當(dāng)前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過(guò)程返回結(jié)果咪奖。
當(dāng)服務(wù)端的onTransact方法被調(diào)用:
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
服務(wù)端通過(guò)code來(lái)確定所請(qǐng)求的目標(biāo)方法盗忱。從data中取出目標(biāo)方法需要的參數(shù),然后執(zhí)行目標(biāo)方法羊赵。
目標(biāo)方法執(zhí)行完趟佃,則往reply中寫入返回值扇谣。
3.3.3 Binder的重連機(jī)制
Binder運(yùn)行在服務(wù)端闲昭,如果服務(wù)端進(jìn)程由于某種原因異常停止罐寨,這個(gè)時(shí)候我們服務(wù)端的Binder連接斷裂(Binder死亡),會(huì)導(dǎo)致我們的遠(yuǎn)程調(diào)用失敗序矩。
Binder的2個(gè)很重要的方法linkToDeath和unlinkToDeath
通過(guò)linkToDeath可以給Binder設(shè)置一個(gè)死亡代理簸淀,當(dāng)Binder死亡時(shí)劲绪,我們會(huì)收到通知,可以重新發(fā)起請(qǐng)求從而恢復(fù)連接祷安。