Android查缺補漏(IPC篇)-- 進程間通訊基礎知識熱身
本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8479282.html
在Android中進程間通信是比較難的一部分,同時又非常重要,針對進程間通信,博主會用四篇文章來介紹,本篇文章為IPC系列的開篇,主要介紹一些IPC中用到的一些概念、基礎等,目的是讓讀者朋友們在學習IPC之前對一些必要的知識有一個大體的把握。在Android中進程間通訊的方式有很多種,在後續的三篇中會分別介紹每一種方式的實現過程已經各自的優缺點。
進程間通訊篇系列文章目錄:
- Android查缺補漏(IPC篇)-- 進程間通訊基礎知識熱身
- Android查缺補漏(IPC篇)-- Bundle、文件共享、ContentProvider、Messenger四種進程間通訊介紹
- Android查缺補漏(IPC篇)-- 款進程通訊之AIDL詳解
- Android查缺補漏(IPC篇)-- 跨進程通訊之Socket簡介及示例
IPC是什麽?
IPC(全稱:Inter-Process Communication)為進程間通訊,指至少兩個進程間傳遞數據或信號的一些技術活方法。
註:進程間通訊是至少兩個進程之間發生的事情,我們通常習慣性的會把一方稱為客戶端,一方稱為服務端,在後續的文章也會多次出現客戶端和服務端,沒接觸過進程間通信的童鞋可能一開始會不太習慣,這裏要註意一下。
為什麽要使用IPC?
無論是在計算機系統還是Android系統中每個進程都有自己一部分獨立的系統資源,彼此是隔離的,為了能是不同的進程互相訪問資源並協同工作,就需要用到進程間通訊。
RPC是什麽?
RPC(Remote Procedure Call)—遠程過程調用,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。(-來自百度百科)
在後面介紹AIDL時會用到RPC的概念,在這裏簡要說明一下RPC在Android的進程間通訊所扮演的角色,以博主本人的理解,簡單來說RPC機制就是指在本地即可調用遠程進程中的方法,而不需要關心其底層實現。
在Android中IPC有哪幾種實現方式?
- Bundle
- 文件共享
- ContentProvider
- Messager
- AIDL
- Socket
如何開啟一個進程
在四大組件的AndroidManifest配置中配置process屬性
比如這個:
<service
android:name=".messager.MessengerService"
android:exported="true"
android:process=":remote" />
“:”開頭和不帶“:”的有什麽區別:
“:”開頭的進程屬於當前應用的私有進程,其他應用的組件不能和它跑在同一進程下。
不帶“:”的進程屬於全局進程,其他應用可以通過ShareUID和它跑在同一進程下。
Android系統會為每一個應用分配一個UID,具有相同的UID才能共享數據。
通過ShareUID跑在同一進程中需要兩個應用有相同的ShareUID並且有相同的簽名才可以。
Android系統為每一個進程分配一個獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間,這就導致不同的虛擬機訪問同一個類的對象會產生多個副本。
使用多進程會導致如下問題:
- 靜態變量和單例失效
- 線程同步機制失效
- SharePreference可靠性下降
- Application多次創建
IPC中涉及到的基礎概念
- Serializable
- Parcelable
- Binder
Serializable
使用Serializable進行序列化很簡單,只需要實現Serializable接口,然後為類指定一個serialVersionUID即可。
Serializable中的serialVersionUID工作機制:
- 序列化時系統會把當前類的serialVersionUID寫入序列化的文件中(或其他中介)
- 反序列化時系統去檢測文件中的serialVersionUID,對比是否和當前類的seralVersionUID一致。
- 一致就說明序列化的類的版本和當前類的版本是相同的,可以成功反序列化,否則就說明當前類和序列化的類相比發生了某些轉換,就會報錯(java.io.InvalidClassException)
- 靜態變量屬於類不屬於對象,不參與序列化過程
- 用transient關鍵字標記的成員變量不參與序列化過程
Parcelable
使用Parcelable進行序列化比Serializable要麻煩一些,需要實現Parcelable接口,並實現一些必要方法,其通常形式如下:
public class Contact implements Parcelable {
public int phoneNumber;
public String name;
public String address;
public Contact(int phoneNumber, String name, String address) {
this.phoneNumber = phoneNumber;
this.name = name;
this.address = address;
}
public Contact() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(phoneNumber);
dest.writeString(name);
dest.writeString(address);
}
public void readFromParcel(Parcel parcel) {
phoneNumber = parcel.readInt();
name = parcel.readString();
address = parcel.readString();
}
public final static Creator<Contact> CREATOR = new Creator<Contact>() {
@Override
public Contact createFromParcel(Parcel source) {
return new Contact(source);
}
@Override
public Contact[] newArray(int size) {
return new Contact[size];
}
};
public Contact(Parcel parcel) {
phoneNumber = parcel.readInt();
name = parcel.readString();
address = parcel.readString();
}
}
一個類只要實現了Parcelable接口,其對象就可以實現序列化並可以通過Intent和Binder傳遞。
- Parcelable中的Parcel內部包含了可序列化的數據,可以在Binder中自由傳輸。
- 序列化功能:writeToParcel實現,最終是通過Parcel中的一系列write方法完成。
- 反序列化:CREATOR完成,通過Parcel的一系列read方法來完成,內部表明了如何創建序列化對象和數組。
- 內容描述:describeContents:僅當當前對象中存在文件描述符時返回1,其余所有情況返回0。
- 反序列化過程需要傳遞當前線程的上下文類加載器,否則會報找不到類的錯誤。
Serializable和Parcelable的區別:
- Serializable是java中的序列化接口,使用簡單,但開銷很大,序列化和反序列化過程需要大量IO操作。
- Parcelable是Android中的接口,使用麻煩,但效率高,首選。
- Parcelable主要適用於內存序列化上,但通過Parcelable將對象序列化到設備中或序列化後通過網絡傳輸也可以,但稍微復雜,建議這種情況用Serializable。
Binder的使用及上層原理
- Binder是Android中的一個類,實現了IBinder接口
- 從IPC角度來說,Binder是一種跨進程通訊方式
- 從android framework角度來說,Binder是ServiceManager鏈接各種Manager(ActvitiyManager、WindowManager等等)和相應ManagerService的橋梁;
- 從android應用層來說,Binder是客戶端和服務端進行通信的媒介,當bindService時,服務端會返回一個包含了服務端業務調用的Binder對象
AIDL中自動生成的Binder接口類的一些方法:
- DESCRIPTOR:Binder的唯一標識,一般用類名
- asInterface(IBinder obj):用於將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象。(如果客戶端和服務端位於同一進程,此方法返回的就是服務端的Stub對象本身,否則返回Stub.proxy)
- asBinder:返回當前的Binder對象
- onTransact:運行在服務端的Binder線程池中
Binder運行在服務端進程,如果服務端進程被異常終止,Binder鏈接就會斷裂,導致我們遠程調用失敗。但是此時我們並不知道Binder鏈接已經中斷,為了解決這個問題,Binder中提供了兩個配對的方法:
- linkToDeath:通過它可以給Binder設置一個死亡代理,當Binder死亡後我們就會收到通知。
如何設置Binder死亡代理:
首先聲明一個DeathRecipeint對象,然後通過binder.linkToDeath()方法將其綁定到binder上。在DeathRecipeint內部有一個方法binderDied,當Binder死亡後,系統就會回調binderDied方法。
示例代碼如下:
private ServiceConnection serviceConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIContactsManager = IContactsManager.Stub.asInterface(service);
Log.i(TAG, "onServiceConnected: mIContactsManager=" + mIContactsManager);
try {
// 給service設置死亡代理
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
...
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 當binder掛掉後就會執行此方法
if (mIContactsManager == null) {
return;
}
// 首先移除之前綁定的死亡代理
mIContactsManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mIContactsManager = null;
// 然後重新綁定遠程服務
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
};
進程間通訊的基礎知識就先介紹到這裏,接下來將開始針對每種進程間通訊方式作出詳細的介紹。
最後想說的是,本系列文章為博主對Android知識進行再次梳理,查缺補漏的學習過程,一方面是對自己遺忘的東西加以復習重新掌握,另一方面相信在重新學習的過程中定會有巨大的新收獲,如果你也有跟我同樣的想法,不妨關註我一起學習,互相探討,共同進步!
參考文獻:
- 《Android開發藝術探索》
源碼地址:本系列文章所對應的全部源碼已同步至github,感興趣的同學可以下載查看,結合代碼看文章會更好。源碼傳送門
本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8479282.html
Android查缺補漏(IPC篇)-- 進程間通訊基礎知識熱身