Android Interface Definition Language (AIDL)
IN THIS DOCUMENT
Defining an AIDL Interface
Create the .aidl file
Implement the interface
Expose the interface to clients
Passing Objects over IPC
Calling an IPC Method
SEE ALSO
Bound Services
AIDL (Android Interface Definition Language) 與其他的介面定義語言相似。定義這個介面,client 和service 可以通過這個介面在進程之間進行通訊。所以說,對象要能在進程之間傳遞,就需要將對象分解成系統能識別的指令。通過代碼實現那個是非常的冗長乏味的,android 系統通過AIDL可以處理。
Note: 希望從不同的應用中訪問service,並且在service需要處理多線程,這種情況下才會使用AIDL。如果不需要從其他的應用中處理IPC,那麼可以實現Binder;如果需要IPC,但是不需要處理多線程,那麼可以使用Messenger。在使用之前,先考慮好哪種方式是適合自己的。
設計AIDL介面之前,需要確認AIDL介面裡面該有的方法。不能假設當調用發生時,thread會伴隨。發生什麼是不同的,取決於該調用是從本地進程或遠程進程的線程發起的。具體來講:
在本地進程中的同一個線程發起調用請求。如果這是你的主UI線程,線程繼續在AIDL介面上執行。如果是其他的線程,它會在service中運行代碼。如果僅僅是本地線程訪問service,可以控制具體的線程在service中執行(如果是在這種情況下,不需要使用AIDL,可以用Binder方式代替).
從遠程進程的線程池中發起調用。需要準備好處理不知線程的同時發起的調用。換句話說,實現AIDL必須保證所有線程的安全性。
The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. If oneway is used with a local call, there is no impact and the call is still synchronous.
Defining an AIDL Interface
--------------------------------------------------------------------------------
可以用Java程式設計語言的文法定義AIDL介面,檔案儲存在原始碼(src/)下,當前應用擁有這個service並且,其他的應用可以綁定這個service。
當構建的應用中含有.aidl檔案,Android SDK 工具根據.aidl檔案能夠產生一個IBinder的介面,儲存在項目的gen/目錄。service應該合理的實現IBinder介面。client 應用可以綁定這個service,並通過IBinder調用service的方法。
用AIDL建立一個綁定的service,基本步驟如下:
建立.aidl檔案
這個檔案定義了介面和方法.
實現介面
Android SDK 工具基於.aidl檔案能夠產生一個介面。介面裡面含有一個名字為Stub的抽象內部類,並且實現了AIDL介面中定義的方法。使用的時候,必須繼承Stub類,實現它的方法。
公開介面
實現service,重寫onBind(), 它的傳回值是stub類。
Caution: 對AIDL介面做的任何改變,必須向後相容,避免其他使用service的應用無法繼續工作。也就是說,因為這個.aidl檔案必須複製到其他的應用中,為了能訪問這個service的介面,必須保證對原始介面的支援。
1. 建立.aidl檔案
AIDL 使用簡單的文法定義介面,可以有一個或者多個方法,這些方法可以傳入參數和傳回值。參數和傳回值可以是任何類型,甚至其他aidl產生的介面。
.aidl檔案必須使用java文法,每一個.aidl檔案只能定義一個單獨的介面,並且只需要介面的聲明和方法的聲明。
一般情況,AIDL支援一下資料類型:
所有的java基本類型 (例如 int, long, char, boolean, 等等)
String
CharSequence
List
List中所有的元素必須是AIDL支援的類型,或者是其他的AIDL介面和定義的parcebles.一個List可以被用來作為一個通常類(例如,List<String>). 實際具體的類接到總是一個ArrayList,雖然該方法產生的時候是List 介面.
Map
Map中所有的元素必須是AIDL支援的類型,或者是其他的AIDL介面和定義的parcebles.通常的Map(例如Map<String,Integer>這種形式的Map是不支援的)。實際具體的類接到總是一個HashMap,雖然該方法產生的時候是Map 介面.
當要匯入的類型,上面沒有列舉,需要import, 即使它們定義在和當前AIDL檔案相同的包裡面。
當定義service的介面時,要意識到:
方法可以有0個或者多個參數,可以返回一個值或者是void.
All non-primitive parameters require a directional tag indicating which way the data goes. Either in, out, orinout (see the example below).
Primitives are in by default, and cannot be otherwise.
Caution: 必須考慮真正需要的資料方向,因為資料裝換是非常昂貴的。
包含在.aidl中所有的注釋在IBinder介面中都會產生(除了在import和package之前的注釋)
僅僅支援方法,不支援靜態成員變數。
下面上.aidl 檔案的例子:
// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}將.aidl檔案儲存在 src/ 目錄下,當編譯項目的時候,, SDK tools 產生IBinder介面檔案,放在項目的gen/ 目錄裡面.
如果您使用的是Eclipse,會迅速的產生Binder類。如果您沒有使用Eclipse,Ant tool 可以在下次構建應用的時候,自動產生binder 類-構建項目的時候需要ant debug (或者ant release), 在寫好.aidl檔案的時候就構建一個次,以便在編碼的時候可以引用產生的類。
2. 實現介面
當您構建項目的時候,Android SDK 工具會產生一個java 介面檔案。 產生的介面檔案中包含一個Stub 的內部抽象類別,這個類實現在.aidl中定義的方法。
Note: Stub 同樣定義了一些幫組的方法,最特別的是 asInterface(), 它需要一個IBinder(通常傳遞給客戶的onServiceConnected()回調方法), 並且返回一個stub介面的執行個體。See the section Calling an IPC Method for more details on how to make this cast.
要實現從.aidl檔案中產生出來的介面,繼承產生的Binder介面(例如,YourInterface.Stub),並且實現從.aidl檔案中繼承來的方法。
這裡是一個通過匿名類的方式實現IRemoteService 介面的例子:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};現在mBinder是Stub類的一個執行個體,下一步,這個介面需要暴露給client調用。
這裡是實現AIDL介面的一些規則:
不能保證是從主線程裡發起的調用,因此在使用的時候,需要考慮多線程啟動和保證service運行時的執行緒安全性。
預設情況,遠程調用是同步的。如果你知道你的service完成的任務需要一些時間,不能從activity的主線程中調用service,因為這樣調用會導致application掛起(應用等待響應),最好在一個新的線程中調用。
Service不會返回任何開發人員自己拋出的異常到調用者。
3. 把介面公開給用戶端
一旦service實現了介面,然後要把它暴露給client,以便clients綁定它。為service公開介面,繼承Service和實現onBind()並返回實現Stub類的執行個體.下面的service例子公開IRemoteService 介面給clients.
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}現在,當client調用bindService()綁定service,Client通過onServiceConnected()回呼函數獲得service onBind()方法的mBinder執行個體。
client可以提供者類,如果client和service在不同的application中,那麼client應用必須拷貝.aidl檔案到自己的src/目錄下。(產生 android.os.Binder 介面—提供給client訪問AIDL 方法).
當client從 onServiceConnected() 中擷取到IBinder對象,就需要調用YourServiceInterface.Stub.asInterface(service) ,將返回的參數轉換成自己的ServiceInterface . 例如:
IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};