AIDL (Android Interface Definition Language) 是一種接口定義語言,用于生成可以在Android設備上兩個進程之間進行進程間通信(interPRocess communication, ipC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數,來完成進程間通信。
簡言之,AIDL能夠實現進程間通信,其內部是通過Binder機制來實現的,后面會具體介紹,現在先介紹AIDL的使用。
AIDL的實現一共分為三部分,一部分是客戶端,調用遠程服務。一部分是服務端,提供服務。最后一部分,也是最關鍵的是AIDL接口,用來傳遞的參數,提供進程間通信。
先在服務端創建AIDL部分代碼。AIDL文件通過如下方式新建一個AIDL文件

默認生成格式
interface IBookManager { /** * 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);}默認如下格式,由于本例要操作Book類,實現兩個方法,添加書本和返回書本列表。定義一個Book類,實現Parcelable接口。
public class Book implements Parcelable { public int bookId; public String bookName; public Book() { } public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } public int getBookId() { return bookId; } public void setBookId(int bookId) { this.bookId = bookId; } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.bookId); dest.writeString(this.bookName); } protected Book(Parcel in) { this.bookId = in.readInt(); this.bookName = in.readString(); } public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() { @Override public Book createFromParcel(Parcel source) { return new Book(source); } @Override public Book[] newArray(int size) { return new Book[size]; } };}由于AIDL只支持數據類型:基本類型(int,long,char,boolean等),String,CharSequence,List,Map,其他類型必須使用import導入,即使它們可能在同一個包里,比如上面的Book。
最終IBookManager.aidl 的實現
// Declare any non-default types here with import statementsimport com.lvr.aidldemo.Book;interface IBookManager { /** * 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); void addBook(in Book book); List<Book> getBookList();}注意:如果自定義的Parcelable對象,必須創建一個和它同名的AIDL文件,并在其中聲明它為parcelable類型。
Book.aidl
// Book.aidlpackage com.lvr.aidldemo;parcelable Book;以上就是AIDL部分的實現,一共三個文件。
然后Make Project ,SDK為自動為我們生成對應的Binder類。
在如下路徑下:
其中該接口中有個重要的內部類Stub ,繼承了Binder 類,同時實現了IBookManager接口。這個內部類是接下來的關鍵內容。
public static abstract class Stub extends android.os.Binder implements com.lvr.aidldemo.IBookManager{}服務端服務端首先要創建一個Service用來監聽客戶端的連接請求。然后在Service中實現Stub 類,并定義接口中方法的具體實現。
//實現了AIDL的抽象函數 private IBookManager.Stub mbinder = new IBookManager.Stub() { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { //什么也不做 } @Override public void addBook(Book book) throws RemoteException { //添加書本 if(!mBookList.contains(book)){ mBookList.add(book); } } @Override public List<Book> getBookList() throws RemoteException { return mBookList; } };當客戶端連接服務端,服務端就會調用如下方法:
public IBinder onBind(Intent intent) { return mbinder; }就會把Stub實現對象返回給客戶端,該對象是個Binder對象,可以實現進程間通信。本例就不真實模擬兩個應用之間的通信,而是讓Service另外開啟一個進程來模擬進程間通信。 <service android:name=".MyService" android:process=":remote"> <intent-filter> <category android:name="android.intent.category.DEFAULT"/> <action android:name="com.lvr.aidldemo.MyService"/> </intent-filter> </service>android:process=":remote"設置為另一個進程。<action android:name="com.lvr.aidldemo.MyService"/>
是為了能讓其他apk隱式bindService。
通過隱式調用的方式來連接service,需要把category設為default,這是因為,隱式調用的時候,intent中的category默認會被設置為default。
客戶端
首先將服務端工程中的aidl文件夾下的內容整個拷貝到客戶端工程的對應位置下,由于本例的使用在一個應用中,就不需要拷貝了,其他情況一定不要忘記這一步。
客戶端需要做的事情比較簡單,首先需要綁定服務端的Service。
Intent intentService = new Intent(); intentService.setAction("com.lvr.aidldemo.MyService"); intentService.setPackage(getPackageName()); intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE); Toast.makeText(getapplicationContext(),"綁定了服務",Toast.LENGTH_SHORT).show();將服務端返回的Binder對象轉換成AIDL接口所屬的類型,接著就可以調用AIDL中的方法了。
if(mIBookManager!=null){ try { mIBookManager.addBook(new Book(18,"新添加的書")); Toast.makeText(getApplicationContext(),mIBookManager.getBookList().size()+"",Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } }二、Binder的工作原理
Binder機制的運行主要包括三個部分:注冊服務、獲取服務和使用服務。其中注冊服務和獲取服務的流程涉及C的內容,由于個人能力有限,就不予介紹了。
本篇文章主要介紹使用服務時,Binder機制的工作原理。
1.Binder對象的獲取
Binder是實現跨進程通信的基礎,那么Binder對象在服務端和客戶端是共享的,是同一個Binder對象。在客戶端通過Binder對象獲取實現了IInterface接口的對象來調用遠程服務,然后通過Binder來實現參數傳遞。
那么如何維護實現了IInterface接口的對象和獲取Binder對象呢?
服務端獲取Binder對象并保存IInterface接口對象Binder中兩個關鍵方法:
public class Binder implement IBinder{ void attachInterface(IInterface plus, String descriptor) IInterface queryLocalInterface(Stringdescriptor) //從IBinder中繼承而來 ..........................}Binder具有被跨進程傳輸的能力是因為它實現了IBinder接口。系統會為每個實現了該接口的對象提供跨進程傳輸,這是系統給我們的一個很大的福利。
Binder具有的完成特定任務的能力是通過它的IInterface的對象獲得的,我們可以簡單理解attachInterface方法會將(descriptor,plus)作為(key,value)對存入Binder對象中的一個Map<String,IInterface>對象中,Binder對象可通過attachInterface方法持有一個IInterface對象(即plus)的引用,并依靠它獲得完成特定任務的能力。queryLocalInterface方法可以認為是根據key值(即參數 descriptor)查找相應的IInterface對象。
在服務端進程,通過實現private IBookManager.Stub mbinder = new IBookManager.Stub() {}抽象類,獲得Binder對象。并保存了IInterface對象。
public Stub(){this.attachInterface(this, DESCRIPTOR);}客戶端獲取Binder對象并獲取IInterface接口對象
通過bindService獲得Binder對象
MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);然后通過Binder對象獲得IInterface對象。
private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { //通過服務端onBind方法返回的binder對象得到IBookManager的實例,得到實例就可以調用它的方法了 mIBookManager = IBookManager.Stub.asInterface(binder); } @Override public void onServiceDisconnected(ComponentName name) { mIBookManager = null; } };其中asInterface(binder)方法如下:
public static com.lvr.aidldemo.IBookManager asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.lvr.aidldemo.IBookManager))) {return ((com.lvr.aidldemo.IBookManager)iin);}return new com.lvr.aidldemo.IBookManager.Stub.Proxy(obj);}先通過queryLocalInterface(DESCRIPTOR);查找到對應的IInterface對象,然后判斷對象的類型,如果是同一個進程調用則返回IBookManager對象,由于是跨進程調用則返回Proxy對象,即Binder類的代理對象。2.調用服務端方法
獲得了Binder類的代理對象,并且通過代理對象獲得了IInterface對象,那么就可以調用接口的具體實現方法了,來實現調用服務端方法的目的。
以addBook方法為例,調用該方法后,客戶端線程掛起,等待喚醒:
@Override public void addBook(com.lvr.aidldemo.Book book) throws android.os.RemoteException{..........//第一個參數:識別調用哪一個方法的ID//第二個參數:Book的序列化傳入數據//第三個參數:調用方法后返回的數據//最后一個不用管mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);_reply.readException();}..........}省略部分主要完成對添加的Book對象進行序列化工作,然后調用transact方法。
Proxy對象中的transact調用發生后,會引起系統的注意,系統意識到Proxy對象想找它的真身Binder對象(系統其實一直存著Binder和Proxy的對應關系)。于是系統將這個請求中的數據轉發給Binder對象,Binder對象將會在onTransact中收到Proxy對象傳來的數據,于是它從data中取出客戶端進程傳來的數據,又根據第一個參數確定想讓它執行添加書本操作,于是它就執行了響應操作,并把結果寫回reply。代碼概略如下:
case TRANSACTION_addBook:{data.enforceInterface(DESCRIPTOR);com.lvr.aidldemo.Book _arg0;if ((0!=data.readInt())) {_arg0 = com.lvr.aidldemo.Book.CREATOR.createFromParcel(data);}else {_arg0 = null;}//這里調用服務端實現的addBook方法this.addBook(_arg0);reply.writeNoException();return true;}然后在transact方法獲得_reply并返回結果,本例中的addList方法沒有返回值。
客戶端線程被喚醒。因此調用服務端方法時,應開啟子線程,防止UI線程堵塞,導致ANR。
關于上述步驟可以簡單用如下方式理解:BpBinder(客戶端)對象和BBinder(服務端)對象,它們都從IBinder類中派生而來,BpBinder(客戶端)對象是BBinder(服務端)對象的代理對象,關系圖如下:

client端:BpBinder.transact()來發送事務請求;server端:BBinder.onTransact()會接收到相應事務。
這樣就完成了跨進程間的通信。以上內容就是當調用遠程方法時,Binder機制所實現的運行過程。通過上面的介紹,就可以理解AIDL的使用,并知道在使用AIDL時,Binder所起到的作用。
新聞熱點
疑難解答