TODO: 待修正
Binder簡介
Binder是android系統中實現的一種高效的IPC機制,平常接觸到的各種XxxManager,以及綁定Service時都在使用它進行跨進程操作。
它的實現基于OpenBinder項目,屬于核心庫。framework層的Binder通信用到的相關java類型都是對應C++類型的一個封裝。
這里framework層就是android提供的java api層,類似jre中的java標準類庫,也就是我們sdk中用到的各種java類型。
IPC和遠程對象(Remotable Object)
在Java編程中,大多數情況下都是進程內的各個對象相互交互,另一些情況,java程序和其它進程進行的通信,就是IPC(inter-process communication)。
廣義上看,像最常見的HTTP網絡通信就是一種跨進程通信——客戶端java程序和遠程服務器上的服務進程通信。所以,其它進程可以是非java程序,如C++程序。抽象的看,不論哪兩種語言的程序進程之間的溝通,都是一些方法調用——可以調用的方法就是所謂的協議,或接口API。
在Java中,一切皆對象,那么在和一個外部進程通信時,首先需要針對它的一組接口描述——也就是方法描述,此描述的類型定義顯然就是通過接口實現了。這樣,此接口所定義的通信協議就是本地java端對外部進程可執行操作的一個描述,實際上其它進程是否是java程序,是否有對象
之說倒不重要,這里站在java程序的角度,虛擬地認為其它進程內部包含一個擁有這些操作的對象——遠程對象——算是本地接口的實現類對象。遠程對象的概念是從某個進程看其它進程的對象而言的。
java程序使用java接口對遠程進程通信協議進行描述
,其它像Swift這樣的語言又有它們自己的通信協議的描述方式。
Binder系統
下面用Binder-SYS表示安卓系統中運行的Binder系統,Binder-IPC表示Binder實現IPC的機制。
Server和Client
Binder-SYS將通信的雙方分為Server和Client,即C/S架構。
兩端進程均使用一個接口IBinder
的實例進行通信,它定義了方法IBinder.transact()
,方法原型:
/**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data to send to the target. Must not be null.
* If you are not sending any data, you must create an empty Parcel
* that is given here.
* @param reply Marshalled data to be received from the target. May be
* null if you are not interested in the return value.
* @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*/
public boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
code
表示要執行的動作,類似Handler發送的Message的what。
code指示了當前遠程操作的命令,IBinder定義了像INTERFACE_TRANSACTION、PING_TRANSACTION這樣的幾個通用命令。自己使用的命令的標識值需要在FIRST_CALL_TRANSACTION和LAST_CALL_TRANSACTION之間,僅僅是整數范圍的一個約定,很好理解。data和reply
data和reply參數相當于普通java方法里的調用參數和返回值。Parcel類型是可以跨進程的數據。flags
參數flags只有0和FLAG_ONEWAY兩種,默認的跨進程操作是同步的,所以transact()方法的執行會阻塞,調用以同步的形式傳遞到遠程的transact(),等待遠端的transact()返回后繼續執行——最好理解的方式就是把兩端的transact()看作一個方法,Binder機制的目標也就是這樣。指定FLAG_ONEWAY時,表示Client的transact()是單向調用,執行后立即返回,無需等待Server端transact()返回。
Server和Client利用IBinder跨進程通信的原理是:
Client調用其IBinder實例的transact()發起操作,Binder-SYS使得方法調用傳遞到Server端,以相同的參數執行Server端IBinder實例的transact()方法——這就是Binder-SYS實現的跨進程操作。
Binder和BinderProxy
Binder-SYS提供了Binder
和BinderProxy
作為IBinder的子類。
每一個Binder對象都會唯一關聯一個BinderProxy對象。
在跨進程通信時,提供服務的Server進程持有一個Binder對象,記為Server_Binder_obj。而其它Client進程持有關聯的BinderProxy對象,記為Client_BinderProxy_obj。不同Client進程里的BinderProxy是不同java對象,而底層是同一個由Binder-SYS維護的C++對象。所以BinderProxy對象實際上是跨進程唯一的。
這里Proxy的含義就是Client進程得到的Server端Binder對象的一個本地引用。
最終Client對Server發起遠程調用的過程就是:
Client調用其BinderProxy實例的transact()發起操作,Binder-SYS使得方法調用傳遞到Server端,以相同的參數執行Server端Binder實例的transact()方法
這里注意下transact()在BinderProxy和Binder中的不同之處:
BinderProxy.transact()方法是Client用來主動發出
遠程操作命令的,它接收code、data參數。BinderProxy是個final類,它的transact()方法只能被調用。
Binder.transact()是用來被動響應
Client發出的遠程調用的。
BinderProxy.transact()調用后,Server端Binder.transact()方法以同樣的code、data參數被調用。
Binder類定義了onTransact()方法來供子類去響應命令,而它的transact()方法調用onTransact()的邏輯。onTransact()更好的表達了Binder的行為。
通信過程
有了以上核心概念,Binder-IPC的原理還是很簡單明了的,一次跨進程遠程通信的過程是:
Client的代碼調用BinderProxy.transact(),發起遠程調用。參數flags為0時方法阻塞,等待Server端對應方法返回后繼續執行。參數flags為FLAG_ONEWAY時立即返回。
Client中的transact()調用傳遞式觸發Server端Binder.transact()的調用,它又調用Binder.onTransact()。
Server端,Binder.onTransact()中,子類的重寫方法根據收到的code,執行對應業務邏輯,設置必要的返回數據到參數reply,然后Binder.transact()方法返回。
Client端,BinderProxy.transact()從阻塞狀態返回,調用者解析得到必要的返回參數,繼續執行。
可以看到,Binder機制維持了Client進程的transact()的調用傳遞給Server端transact()以及相應的調用返回的傳遞過程。注意參數flags為FLAG_ONEWAY時指定通信為“單向”的,這樣整個遠程調用就成為了異步的——Client的transact()會很快返回,不需要等待Server端的方法調用完成(甚至是開始)。
以上就是Binder-IPC的主要原理。
示例項目:StudentManager
下面提供一個示例項目來說明如何使用Binder-IPC完成跨進程通信。
案例提供這樣的功能:
Server端程序實現了學生管理功能,提供了按學號查詢學生年齡的操作。
然后Client通過Binder-IPC對Server端執行遠程調用獲得結果。
協議部分
從編程角度看,使用Binder-IPC實現Client和Server通信,有一些類型它們都會用到,這些類型僅僅和通信相關,而不涉及實際業務。這里稱它們為通信協議相關類型。
一般都是提供服務的Server端定義這些類型,下面就依次實現它們。
通信接口
它定義了Server端可接收的方法調用,也就是Client可以發起的遠程調用。
這里根據假設的需求,定義下面的接口:
package com.idlestars.binderdemo.serviceapi;
...
interface IStudentManager extends IInterface {
String DESCRIPTOR = "com.tiro.binder.StudentManagerStub";
int TRANSACTION_GET_AGE = (IBinder.FIRST_CALL_TRANSACTION + 0);
int getAge(int studentId) throws RemoteException;;
}
通信接口是偏業務上的,它定義了方法getAge()用來根據學生id來獲取其年齡。下面對它進行一些說明。
RemoteException
所有接口方法需要聲明RemoteException異常,跨進程操作時目標服務進程總可能意外終止,或者服務類調用Parcel.writeException()來通知異常發生,這樣Client端接口方法的調用就拋出RemoteException。DESCRIPTOR
對當前接口的一個字符串標識,Binder類的attachInterface()和queryLocalInterface()方法會用到它。TRANSACTION_GET_AGE
對應接口方法getAge()的命令code。IInterface
Binder-IPC要求的標準實現方式是,通信接口需要繼承接口IInterface。它定義了asBinder()方法用來返回接口實例關聯的IBinder對象。
這是因為一般正是Server端的Binder子類會實現通信接口,然后,Client是無法拿到Server端的IStudentManager對象的,所以,為Client定義一個本地的IStudentManager的代理實現類,該實現類使用BinderProxy調用Server端方法獲取結果。
也就是通常兩端實現接口IStudentManager的地方都密切關聯了一個可以用來遠程通信的IBinder對象,而asBinder()就是用來返回這個IBinder的。
經試驗,這不是必須的。
Client端代理類
有了通信接口后,緊接著需要實現Server和Client使用IBinder進行數據交互的部分。也就是transact()的邏輯。
IStudentManager的實現是位于Server端的,Client端無法得到其對象。所以,Client端定義一個接口的代理實現類:
package com.idlestars.binderdemo.serviceapi;
...
public class StudentManager implements IStudentManager {
IBinder mRemote;
public StudentManager(IBinder remote) {
mRemote = remote;
}
@Override
public int getAge(int studentId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(studentId);
mRemote.transact(TRANSACTION_GET_AGE, data, reply, 0);
reply.readException();
int age = reply.readInt();
return age;
} finally {
data.recycle();
reply.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
代理類StudentManager對getAge()的實現是通過mRemote來發送遠程調用。
此處的mRemote是連接到Server時,Server端Binder關聯的BinderProxy對象。
上面getAge()中寫入和讀取數據的順序必須和Server端的Binder.onTransact()對應——主要就是code的取值和data、reply中數據的寫入讀取順序。
StudentManager實現的IInterface.asBinder()返回它組合的mRemote。
Server端通信類
應用層提供可供其它進程訪問的服務的方式就是通過Service組件,Service組件所在進程就是Server進程。
Client進程使用bindService()來和Server進程進行通信。
所以這里需要準備onBind()返回的Binder對象。同進程內的bindService()調用會返回給調用者Binder對象本身,而其它進程的調用最終得到的是Binder關聯的BinderProxy對象。總之這個Binder子類就是服務綁定者后續和服務進行通信的渠道。
Server端使用Binder進行通信,也就是是響應transact()調用。
一般為了讓返回的Binder對象可以被同進程內的綁定者當做IStudentManager去使用,這里定義的Binder子類就同時實現IStudentManager。
在Binder.onTransact()中將會實現和Client使用的代理類StudentManager.transact()對應的通信數據交換邏輯。
這部分邏輯是和IStudentManager定義的業務方法無關的。
為了方便將Binder.onTransact()的邏輯暴漏給Client——因為Client程序會最終引用這些類型,不論是源碼方式還是庫引用,處于安全或方便的目的,這里先定義一個抽象的Binder子類StudentManagerStub,它完成了和StudentManager交換數據,響應遠程調用的邏輯。作為一個抽象類,它實現接口IStudentManager,但不去做任何實際處理,onBind()返回的類型會繼承它,并實現IStudentManager的方法。
public abstract class StudentManagerStub extends Binder implements IStudentManager {
public StudentManagerStub() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_GET_AGE:
{
data.enforceInterface(DESCRIPTOR);
int stuId = data.readInt();
reply.writeNoException();
reply.writeInt(getAge(stuId));
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
public static IStudentManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IStudentManager in =
(IStudentManager)obj.queryLocalInterface(DESCRIPTOR);
if (in != null) {
return in;
}
return new StudentManager(obj);
}
}
可以看到StudentManagerStub.onTransact()和StudentManager.transact()是對應的,后者的每一個code和前者都有一個case語句去處理。重要的是,有關data、reply參數的數據的寫入和讀取順序也嚴格對應。
在onTransact()中調用了getAge()方法,它由最終的StudentManagerStub的子類去完成。getAge()是業務方法,和通信細節沒有關系。
StudentManagerStub作為Binder子類,它實現的IInterface.asBinder()直接返回其對象本身——它既是IInterface的子類,又是Binder子類。
小結
以上的IStudentManager、StudentManager、StudentManagerStub是協議部分涉及到的類型。它們由Server端程序提供,Client端去引用。
StudentManagerStub的定義,使得Server端通信協議實現和通信接口的業務方法的實現得以分離。
有關協議類型要注意下面幾點
數據支持
IBinder.transact()可傳遞的數據只能是Parcel類型的,關于它可攜帶的數據類型參見其API。
Parcel對象可以攜帶的類型決定了通信所能支持的數據類型。通信規則
Client傳遞的調用參數data和Server端返回的reply數據,它們在寫入和讀取的順序上必須是一致的。
寫入和讀取的順序也是通信規則的一部分,所以兩端的transact()、onTransact()邏輯嚴格對應。
業務部分
除了Binder-IPC需要的通信協議相關的類型,Server端需要提供Service來供其它進程綁定。并且它自己實現IStudentManager的業務方法。
Client中會使用代理類StudentManager來訪問服務。
定義Service
Server提供一個Service組件來供自身程序、其它程序等訪問:
public class RemoteService extends Service {
StudentManagerService mBinder = new StudentManagerService();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
為了突出重點,RemoteService的代碼非常簡單。就是返回一個StudentManagerService對象。
額外的工作就是在AndroidManifest.xml中注冊它。
注意onBind()的機制是僅在第一個bindService()的請求時返回關聯的IBinder對象,之后不再調用。
所以,一個Service在運行期間也只能通過一個IBinder對象和各個Client通信。
實現IStudentManager
onBind()返回的StudentManagerService繼承自StudentManagerStub,它只需要完成實際的getAge()方法。
StudentManagerStub已經實現了通信協議的邏輯。
class StudentManagerService extends StudentManagerStub {
@Override
public int getAge(int studentId) throws RemoteException {
return Math.abs(studentId % 30);
}
}
Client訪問Service
Client端訪問目標Service的步驟如下:
- 調用bindService()。
- 將返回的IBinder使用StudentManagerStub.asInterface()轉換為IStudentManager的實例。
- 像普通接口那樣使用IStudentManager。
可以看到,其實對Client端而言,它只需要知道IStudentManager即可,而
StudentManagerStub和StudentManager直接由提供服務類的Server端定義即可。
在不同程序間進行Service的訪問時,由誰來提供這些通信協議的類型是一個說一不二的事情。
使用bindService()來綁定服務是很基礎的事情了。
如果是同進程,那么onServiceConnected()返回的是上面onBind()返回的StudentManagerService對象。
如果是不同進程,返回的是onBind()返回的StudentManagerService對象關聯的BinderProxy對象。
所以bindService()得到的IBinder是和Service是否在同一個進程密切相關的。
上面StudentManagerStub.asInterface()方法正是用來將bindService()得到的IBinder轉換為最終要用到的IStudentManager的實例:
public static IStudentManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IStudentManager in =
(IStudentManager)obj.queryLocalInterface(DESCRIPTOR);
if (in != null) {
return in;
}
return new StudentManager(obj);
}
和Service同進程
參數obj就是StudentManagerService,因為使用的是IStudentManager.DESCRIPTOR,它的queryLocalInterface()返回StudentManagerService對象本身,成功強轉為IStudentManager。和Service不同進程
參數obj是BinderProxy對象,上面if語句不會執行,這時,Client進程得到一個組合了此obj的StudentManager代理類對象,StudentManager負責遠程調用Server端的方法。
這里使用queryLocalInterface()而不是instanceof這類方式執行類型檢查,因為不能依賴到
StudentManagerService,包括對類型名稱的假設。
而接口標識這樣的方式可以滿足上面的IStudentManager類型在Server進程內,或Client端兩種轉換要求。
實際上方法asInterface()放在協議類型StudentManagerStub、StudentManager中都可以。
Client訪問Service的代碼:
IStudentManager mStudentManager;
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mStudentManager = StudentManagerStub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mStudentManager = null;
}
};
private void bindToService() {
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
bindService()的使用不過多介紹,intent可以指定為Action字符串。
執行遠程調用
bindService()獲得一個IStudentManager對象后,就可以用它來調用Server端的方法了。
實際上,Client端的代碼調用getAge()方法和其它java方法沒什么兩樣,唯一的區別就是需要捕獲可能的RemoteException異常。
下面的方法onGetAgeClick()是按鈕點擊處理,它讀取輸入的stuId作為參數,執行getAge(),將返回結果顯示。
public void onGetAgeClick(View view) {
if (mStudentManager != null) {
try {
int stuId = Integer.valueOf(et_stu_id.getText().toString());
int age = mStudentManager.getAge(stuId);
tv_age.setText("Age: " + age);
} catch (Exception e) {
Log.d("Binder", "getAge Exception: " + e.getMessage());
}
}
}
Binder機制的不同使用方式
需要注意的一點是,以上關于Binder架構討論的種種都是針對典型的IPC的情形的。
不過IBinder的使用在app層一般都是結合作為四大組件之一的Service進行的,也就是bindService()。Service可以是其它app的Service類,或者是當前程序本身的Service類,可以運行在相同進程或其它進程。
非Service方式的使用
framework層的ActivityManager、PackageManager等都是利用了Binder機制。但它們的服務類不是通過Service組件去提供。
以IActivityManager為例:
- 接口IActivityManager定義了有關管理系統中所有Activity的API。
- ActivityManagerService繼承自Binder,并實現了IActivityManager,相當于一個Server端的服務類。
- SystemServer進程啟動后,它實例化ActivityManagerService對象,然后注冊到ServiceManager中。
- ActivityManager通過ServiceManager獲得ActivityManagerService的BinderProxy,它也實現了IActivityManager,
相當于一個代理類。
可見,像系統服務它們位于特殊的進程內,不是以Service組件的方式運行。
其注冊和獲取的方式也不是bindService()這樣的,而是依賴ServiceManager。
這里簡單的舉例Binder的非Service使用形式,說明一點,Binder機制提供“更底層”的API,bindService()方式訪問Service組件時用到Binder架構,在app開發中這是主要形式。但可以脫離Service來使用Binder進行IPC通信。
SystemServer作為系統進程它的生命周期必然會更穩定,而Service對進程的“重要性”的影響顯然還不如Activity。
在使用Binder來進行IPC時,總要關心目標進程的存活狀態,后面會再涉及到這點。
Service方式
此時分兩種情況:目標Service在同一進程和不同進程。綁定其它程序的Service肯定是不同進程的情況了。而當前項目里的Service也可以通過在AndroidManifest.xml中聲明不同的android:process
來使其運行在另外進程中。
同進程內
這種是最簡單的bindService()的使用場景。
此時,Service.onBind()返回的就是Binder的子類。它是同進程的對象,而且類型已知。
Service返回的Binder類是可以完全訪問Service的功能的,為了使用此Binder和Service通信,有以下方式:
- Binder子類提供一些public方法供調用,這些方法操作Service。
- Binder子類返回對應的Service對象供使用。
- Binder子類返回寄宿在Service中的一些公開成員對象供訪問,一般就是此Service提供的不同接口的實現類對象。
此時不需要標準的Client端,bindService()的調用者得到Binder后就可以訪問Service提供的各項功能了。
綁定同程序不同進程中的Service
此時從另一個進程獲取到的是BinderProxy對象,屬于標準的IPC方式。
bindService()的代碼最終獲得的是代理類,而不是Binder對象本身。
此時的使用方式和上面StudentManager示例完全一樣。
因為是同一個程序,涉及的相關類型無需顯示引用。
綁定其它程序的Service
其它程序必然運行在其它進程。這時,通信協議相關的IStudentManager、StudentManagerStub和
StudentManager這些類型都是目標程序提供的。
自己的項目中需要引用這些類型,之后的使用方式和上面StudentManager示例一樣。
向Server端注冊回調
除了可以調用Server端的業務接口方法外,還可以向Server注冊回調接口,這樣就相當于實現了跨進程的觀察者模式。或者是類似廣播監聽器那樣的效果。
文章底部“案例代碼”中的RemoteService示例演示了如何注冊回調接口道其它進程。
示例中的通信接口如下:
interface IRemoteServiceCallback {
void valueChanged(int value);
}
interface IRemoteService {
void registerCallback(IRemoteServiceCallback cb);
void unregisterCallback(IRemoteServiceCallback cb);
}
代碼演示了Client使用IRemoteService將自己實現的IRemoteServiceCallback對象注冊到Server。
之后Server端就可以調用valueChanged()方法通知Client。
實際上IRemoteServiceCallback對象肯定是無法跨進程傳遞的,valueChanged()的調用又是一次IPC過程,
此時Client作為IRemoteServiceCallback的實現方,而Server作為IRemoteServiceCallback方法的調用方,valueChanged()的遠程調用的過程中,原Server是Client,原Client是Server。
可見Binder-IPC進行的通信中,Server和Client是相對概念。對應一個通信接口,誰發起遠程調用,誰就是Client。
registerCallback()傳遞給Server的是當前Client實現IRemoteServiceCallback所定義的Binder子類對象的BinderProxy。考慮許多Client向Server注冊回調接口的情形,最終的,Server端維護了這些Client提供的
IRemoteServiceCallback實例關聯的IBinder對象,Server通過這些IBinder對象通知Client,達到調用其valueChanged()方法。
類android.os.RemoteCallbackList
正是用來管理遠程回調接口的,它就是以這些接口實例關聯的IBinder作為標識。所以可以用來跨進程唯一標識不同Clients注冊的IRemoteServiceCallback對象。
監聽Server端IBinder的死亡
當Client端持有的IBinder代表著的其它進程中的服務類時,就需要特別關注其是否仍然有效——一般就是其所在進程是否還在運行決定。有下面三個方法用來完成對遠程對象的檢查:
- The
transact()
method will throw a RemoteException exception if you try to call it on an IBinder whose process no longer exists. - The
pingBinder()
method can be called, and will return false if the remote process no longer exists. - The
linkToDeath()
method can be used to register a IBinder.DeathRecipient with the IBinder, which will be called when its containing process goes away.
使用bindService()時都知道,目標Service所在進程終止時,ServiceConnection.onServiceConnected()方法就會執行(總會在main線程中執行)。
因為使用IBinder的方式不限于Service中,所以在IBinder類的級別上也可以去主動監聽服務類對象的死亡。
前面在IBinder的說明中提到它提供了幾個方法用來檢查目標Binder是否存活,而主動獲取其死亡通知的方式
就是注冊IBinder.DeathRecipient到代理類持有的IBinder。
Client在獲取到IBinder后:
binder.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// 做一些清理,或者是嘗試重新激活目標服務進程
}
}, 0);
linkToDeath()會在binder線程池中的線程中執行,第二個參數flags文檔中竟然沒有說明?
線程問題
當Server和Client處在同一個進程或不同進程中時,transact()的執行所在的線程會是不同的表現。
不同進程
Client端
Client端是發起transact()的地方,其方法調用者所在線程就是transact()的執行線程。
前面的知識已經明確知道了transact()可以是同步或異步的,如果是同步的形式,而Server端的transact()很可能會耗時,那么Client端的transact()就不應該在UI線程中執行。Server端
服務類對象所在的Server進程會維護一個binder專用的線程池來處理來自Client的請求。
所以Server端的transact()的執行總是異步的,因此還需要注意線程同步的問題。
不論Client的transact()的參數flags指定為0或0和FLAG_ONEWAY,Server端transact()總是在某個線程池的線程中執行。flags僅僅影響Client端transact()是否立即返回。
同一個進程
一般若自己的app內部bindService()來訪問進程內的Service時,就屬于同進程的C/S通信。
此時Client獲取到的IBinder就是服務類對象本身,所以,最終Client和Server端的transact()的執行是同一個方法的調用!!
Binder的其它形式
手動去實現Binder-IPC用到的各種類型非常繁瑣,而且如果對Binder的使用不需要非常多的控制時,可以利用下面的AIDL和Messenger方式完成IPC,它們都是對Binder使用的一個簡化。
AIDL
AIDL是方便定義通信協議的一個工具,注意它的工作過程是屬于項目的構建的一部分。
使用aidl時,需要用它來定義通信的接口,聲明用到的自定義的數據格式。
之后工具會自動生成java文件,作為最終編譯的代碼的一部分。
其生成的類型包括:
通信接口
類似上面的IStudentManager,一個繼承IInterface的接口。Server端通信基類
如上面的StudentManagerStub,它實現onTransact()的邏輯,用來響應不同命令,會調用業務接口IStudentManager的對應方法得到執行結果。
作為協議的一部分,它不去實現接口的業務方法。
onTransact()實現了Server端通信的細節。
最終的服務類繼承“Server端通信基類”,并實現通信接口里業務方法的邏輯。
- 代理類
如上面的StudentManager,它封裝一個BinderProxy與遠程服務類對象進行通信。
作為一個工具,它的功能就是簡化開發過程。和上面實現Server端、Client端相關類型的最終效果是一樣的。
更多細節參見api文檔aidl的介紹,文章底部資料部分給出了具體地址。
Messenger
在創建一個可綁定的Service時,有三種方式可以提供onBind()返回的IBinder對象:
返回Binder子類
這種方式只有同進程時才可行,否則會返回給其它進程Binder的代理BinderProxy對象。AIDL
特點是跨進程,且Server端transact()的執行是由Binder線程池里的線程調用的,需要進行并發控制。Messenger
這種方式下,通信的命令被封裝為Message對象,而且Server端使用一個Handler來依次處理收到的命令,不存在并發。
使用Messenger的步驟如下:
- The service implements a Handler that receives a callback for each call from a client.
- The Handler is used to create a Messenger object (which is a reference to the Handler).
- The Messenger creates an IBinder that the service returns to clients from onBind().
- Clients use the IBinder to instantiate the Messenger (that references the service's Handler), which the client uses to send Message objects to the service.
- The service receives each Message in its Handler—specifically, in the handleMessage() method.
這種方式下,Client和Server之間沒有接口方法,而是由Client發送Message表示的命令被Server端處理。
可見Messenger簡化了transact()的過程為handler發送消息的方式,Messenger的底層依然是標準的binder IPC方式實現的,Messenger的作用就類似上面的代理類,不過它實現的是通用的IMessenger接口。
如果需要Server端向Client發送Message,在Client中定義好Handler和Messenger,然后把Messenger設置給發送到Server的Message對象的replyTo字段。
IPC權限
如果Server端需要對訪問它的Client做權限檢查,那么可以在AndroidManifest.xml中首先定義好權限。
方法android.content.ContextWrapper#checkCallingOrSelfPermission()
可以用來檢查調用者是否擁有指定的權限。
如果使用的是Service,那么onBind()處可以控制是否返回IBinder給某個Client,但是onBind()只會被執行一次,若返回null的話,后續的onServiceConnected()也不會執行。這里沒有試驗這種默認行為是否影響其它進程對服務的綁定,但終究不靈活。
在onTransact()方法中,Binder提供了getCallingPid()和getCallingUid()來獲得當前transact()執行時調用者的進程和用戶等信息。在這里,進行權限檢查,然后選擇性返回false的話會更好些,也就是說,針對一次具體的transact()操作可以拒絕響應,但不影響其它的transact()調用。
補充
跨進程遞歸調用
The Binder system also supports recursion across processes. For example if process A performs a transaction to process B, and process B while handling that transaction calls transact() on an IBinder that is implemented in A, then the thread in A that is currently waiting for the original transaction to finish will take care of calling Binder.onTransact() on the object being called by B. This ensures that the recursion semantics when calling remote binder object are the same as when calling local objects.
Binder對象的生命周期
Binder class is just a basic IPC primitive; it has no impact on an application's lifecycle, and is valid only as long as the process that created it continues to run. To use this correctly, you must be doing so within the context of a top-level application component (a Service, Activity, or ContentProvider) that lets the system know your process should remain running.
You must keep in mind the situations in which your process could go away, and thus require that you later re-create a new Binder and re-attach it when the process starts again. For example, if you are using this within an Activity, your activity's process may be killed any time the activity is not started; if the activity is later re-created you will need to create a new Binder and hand it back to the correct place again; you need to be aware that your process may be started for another reason (for example to receive a broadcast) that will not involve re-creating the activity and thus run its code to create a new Binder.
案例代碼
文章源碼
上述代碼可以在這里獲取到:
https://github.com/everhad/AndroidBinderDemoApiDemos中:RemoteService示例
項目地址:
https://git.oschina.net/idlestar/AndroidApiDemos
文件路徑:
src/main/java/com/example/android/apis/app/RemoteService.java
- ActivityManager相關源碼
資料
bound-services
/docs/guide/components/bound-services.htmlaidl
/docs/guide/components/aidl.html
(本文使用Atom編寫)
文章列表