AIDL:Android Interface Definition Language,即 Android 接口定義語言。java
Android 系統中的進程之間不能共享內存,所以,須要提供一些機制在不一樣進程之間進行數據通訊。 linux
爲了使其餘的應用程序也能夠訪問本應用程序提供的服務,Android 系統採用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與不少其餘的基於 RPC 的解決方案同樣,Android 使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。咱們知道 Android 四大組件中的 3 種(Activity、BroadcastReceiver和ContentProvider)均可以進行跨進程訪問,另一種 Android 組件 Service 一樣能夠。所以,能夠將這種能夠跨進程訪問的服務稱爲 AIDL(Android Interface Definition Language)服務。android
在介紹 AIDL 的使用以及其它特性前,咱們先來了解下 AIDL 的核心——Binder。服務器
看過一些關於 Binder 的文章,總得來講 Binder 機制的底層實現很複雜,至關複雜,要徹底搞清楚,得花大量的時間。從某種角度來講,我的以爲,對於 Binder,咱們只須要了解其上層原理以及使用方法便可。app
直觀來看,從代碼的角度來講,Binder 是 Android 系統源碼中的一個類,它實現了 IBinder 接口;從 IPC 角度來講,Binder 是 Android 中的一種跨進程通訊方式;從 Android Framework 角度來說,Binder 是 ServiceManager 鏈接各類 Manager(ActivityManager、WindowManager 等等)和相應 ManagerService 的橋樑;從 Android 應用層來講,Binder 是客戶端和服務端進行通訊的媒介,當 bindService 的時候,服務端會返回一個包含了服務端業務調用的 Binder 對象,經過這個 Binder 對象,客戶端就能夠和服務端進行通訊,這裏的服務包括普通服務和基於 AIDL 的服務。異步
接下來,咱們經過一個 AIDL 示例,來分析 Binder 的工做機制。在工程目錄中新建一個名爲 aidl 的 package,而後新建 Book.Java、Book.aidl(建立此文件時,as 會提示已存在,須要先用其它命令,建立成功後再重命名爲 Book.aidl )和 IBookManager.aidl,代碼以下:ide
// Book.java package com.cy.ipcsample.aidl; import android.os.Parcel; import android.os.Parcelable; /** * 數據類 * @author cspecialy * @version v1.0.0 * @date 2018/5/14 21:38 */ public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(bookId); parcel.writeString(bookName); } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", bookName='" + bookName + '\'' + '}'; } }
// Book.aidl package com.cy.ipcsample.aidl; parcelable Book;
// IBookManager.aidl package com.cy.ipcsample.aidl; import com.cy.ipcsample.aidl.Book; import com.cy.ipcsample.aidl.IOnNewBookArrivedListener; interface IBookManager { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnNewBookArrivedListener listener); void unRegisterListener(IOnNewBookArrivedListener listener); }
建立好三個文件以後,編譯一下,as 會在 app/build/generated/source/aidl/debug
目錄下的 com.cy.ipcsample 包中生成一個名爲 IBookManager.Java 的類,以下圖所示:函數
這是系統生成的 Binder 類,接下來咱們要利用這個類來分析 Binder 的工做原理。其代碼以下:(生成的代碼格式很亂,能夠格式化代碼以後看)ui
/* * This file is auto-generated. DO NOT MODIFY. * Original file: G:\\Android\\Github\\Bugly-Android-Demo\\sample\\ipcsample\\src\\main\\aidl\\com\\cy\\ipcsample * \\aidl\\IBookManager.aidl */ package com.cy.ipcsample.aidl; public interface IBookManager extends android.os.IInterface { public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException; public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException; /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.cy.ipcsample.aidl.IBookManager { static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); private static final java.lang.String DESCRIPTOR = "com.cy.ipcsample.aidl.IBookManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.cy.ipcsample.aidl.IBookManager interface, * generating a proxy if needed. */ public static com.cy.ipcsample.aidl.IBookManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.cy.ipcsample.aidl.IBookManager))) { return ((com.cy.ipcsample.aidl.IBookManager) iin); } return new com.cy.ipcsample.aidl.IBookManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getBookList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.cy.ipcsample.aidl.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(DESCRIPTOR); com.cy.ipcsample.aidl.Book _arg0; if ((0 != data.readInt())) { _arg0 = com.cy.ipcsample.aidl.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.cy.ipcsample.aidl.IBookManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public android.os.IBinder asBinder() { return mRemote; } @Override public java.util.List<com.cy.ipcsample.aidl.Book> getBookList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.cy.ipcsample.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.cy.ipcsample.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.cy.ipcsample.aidl.Book book) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((book != null)) { _data.writeInt(1); book.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } } }
可見,系統爲咱們生成了一個 IBookManager 接口,它繼承了 IInterface 這個接口,因此這裏要注意下,全部能夠在 Binder 中傳輸的接口,都須要繼承 IInterface 接口。this
接下來分析下,該類的工做機制。仔細看,能夠發現,該類主要分紅三個部分:
第一部分咱們不用管它,主要看 Stub 類和 Proxy 類。在 Stub 中,首先聲明瞭兩個用於標識 IBookManager 方法的整型變量,這兩個變量用於標識在 transact 過程當中客戶端所請求的是哪一個方法。接着,是 asInterface 方法,該方法用於將服務端的 Binder 對象轉換成客戶端所需的 AIDL 接口類型的對象,該方法經過調用 Binder 的 queryLocalInterface 方法,判斷客戶端和服務端是否處於同一進程,若是客戶端、服務端處於同一進程,那麼此方法直接返回服務端的 Stub,不然,返回 Stub 的代理對象 Proxy。queryLocalInterface 實現以下:
/** * Use information supplied to attachInterface() to return the * associated IInterface if it matches the requested * descriptor. */ public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor.equals(descriptor)) { return mOwner; } return null; }
mOwner 是在 Stub 構造函數中傳進去的 this 參數。
接下來是 Stub 的代理類 Stub.Proxy,由上面的分析可知,Stub.Proxy 是運行在客戶端的(由 asInterface 方法返回給客戶端的對象),Stub.Proxy對象建立後,持有服務端的 Binder 對象,用於客戶端請求時調用服務端方法進行遠程調用。客戶端在向服務端發起請求時,調用 Stub.Proxy 的相應方法,Stub.Proxy 方法的流程以下:
以上,就是系統生成的 IBookManager 的工做過程,須要注意下,服務端的 onTransact 方法是運行在 Binder 線程池中的。因爲 IBookManager.Stub 類繼承 Binder,因此上述分析即 Binder 的工做機制,簡單總結下:
或者參考下圖理解下:
因而可知,Binder 在 AIDL 中承載着重要的職能,是 AIDL 的核心,理解了 Binder 的工做機制,其實在不少方面都頗有用。
一套 AIDL 服務搭建的步驟以下:
接下來,咱們使用上面的 IBookManager 來實現 AIDL。
直接使用上面建立好的 Book.aidl、IBookManager.aidl 文件便可
建立一個 Service,命名爲 BookManagerService,代碼以下:
package com.cy.ipcsample.aidl import android.app.Service import android.content.Intent import android.os.IBinder import android.os.RemoteCallbackList import android.util.Log import java.util.concurrent.CopyOnWriteArrayList class BookManagerService : Service() { private val TAG = "BookManagerService" private val mBookList = CopyOnWriteArrayList<Book>() private val mListenerList = RemoteCallbackList<IOnNewBookArrivedListener>() /** * 實現 AIDL 接口的 Binder 對象,客戶端綁定服務端時,直接返回此 Binder 對象 */ private val mBinder = object : IBookManager.Stub() { override fun getBookList(): MutableList<Book> { return mBookList } override fun addBook(book: Book?) { mBookList.add(book) } } override fun onBind(intent: Intent): IBinder { return mBinder } override fun onCreate() { super.onCreate() // 建立兩本圖書 mBookList.add(Book(1, "Android")) mBookList.add(Book(2, "iOS")) } } 而後在 AndroidManifest 中註冊 Service,注意啓動多進程: <service android:name=".aidl.BookManagerService" android:process="com.cy.ipcsample.bookManagerService"> </service>
客戶端的建立,直接使用 Activity 便可,綁定遠程服務的代碼以下:
private val mConnection = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { } /** * 鏈接遠程服務成功的回調 */ override fun onServiceConnected(name: ComponentName?, service: IBinder?) { } } // 綁定遠程服務 bindService(Intent(this, BookManagerService::class.java), mConnection, Context.BIND_AUTO_CREATE)
與服務器綁定成功後,首先在 ServiceConnection 的回調中,將服務端返回的 Binder 對象轉換成 AIDL 接口所屬的對象,就能夠調用相應方法和服務端通訊了,代碼以下:
// 將服務端返回的 Binder 對象轉換成 IBookManager 對象 val bookManager = IBookManager.Stub.asInterface(service) // 與服務端通訊 try { // 獲取圖書列表 val list = bookManager.bookList Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}") Log.i(TAG, "query book list: $list") // 添加一本圖書 val book = Book(3, "Android開發藝術探索") bookManager.addBook(book) Log.i(TAG, "add book: $book") // 獲取圖書列表 val newList = bookManager.bookList Log.i(TAG, "query book list: $newList") } catch (e: RemoteException) { e.printStackTrace() }
代碼中,咱們先查詢了服務端的圖書列表,接着向服務端添加一本書Android藝術開發探索
,而後再次查詢看是否添加成功。運行下看 log,以下圖所示:
可見,運行結果和預期結果一致。完整的客戶端代碼以下:
package com.cy.ipcsample.aidl import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.os.RemoteException import android.support.v7.app.AppCompatActivity import android.util.Log import com.cy.ipcsample.R class BookManagerActivity : AppCompatActivity() { private val TAG = "BookManagerActivity" private val mConnection = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { Log.d(TAG, "binder died.") } /** * 鏈接遠程服務成功的回調 */ override fun onServiceConnected(name: ComponentName?, service: IBinder?) { // 將服務端返回的 Binder 對象轉換成 IBookManager 對象 val bookManager = IBookManager.Stub.asInterface(service) // 與服務端通訊 try { // 獲取圖書列表 val list = bookManager.bookList Log.i(TAG, "query book list, list type: ${list.javaClass.canonicalName}") Log.i(TAG, "query book list: $list") // 添加一本圖書 val book = Book(3, "Android開發藝術探索") bookManager.addBook(book) Log.i(TAG, "add book: $book") // 獲取圖書列表 val newList = bookManager.bookList Log.i(TAG, "query book list: $newList") } catch (e: RemoteException) { e.printStackTrace() } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_book_manager) // 綁定遠程服務 bindService(Intent(this, BookManagerService::class.java), mConnection, Context.BIND_AUTO_CREATE) } override fun onDestroy() { unbindService(mConnection) super.onDestroy() } }
到此,一個簡單的 AIDL 示例就完成了,固然,AIDL 的使用遠沒有那麼簡單,還有不少情景須要考慮的,好比:客戶端須要隨時服務端在狀態變化時同時客戶端,相似觀察這模式,那麼訂閱與反訂閱怎麼實現;Binder 意外死亡,怎麼重連等等,更多內容,能夠參考《Android藝術開發探索》,電子書下載
AIDL 的調用過程是同步仍是異步的?
這個問題其實看這一節的標題你們都知道了,AIDL 的調用過程是同步的。同時,上面分析 Binder 的機制時,也提到了,客戶端進行遠程 RPC 請求時,線程會掛起,等待結果,由此也可知,AIDL 的調用過程是同步的,下面來驗證下。
首先,在服務端的 BookManagerService 中實現的 Binder 對象的 getBookList 方法添加延時執行,以下圖所示:
而後,在客戶端的 BookManagerActivity 中添加一個按鈕,點擊按鈕時調用服務端 Binder 的 getBookList 作 RPC 請求。代碼以下:
運行後,連續點擊按鈕,結果以下圖所示:
由圖所知,連續點擊按鈕事後一段時間,出現了無響應錯誤( ANR ),由此可知,客戶端向服務端作 RPC 請求時,是同步的,也就是說:AIDL 的調用過程是同步的。
AIDL 的調用過程是同步的,當咱們須要服務端作耗時操做時,確定是不能使用同步調用的,不然輕者影響用戶體驗,重者直接 ANR 或者應用崩潰。那麼如何使 AIDL 的調用過程是異步的呢?
其實也很簡單,只須要把調用放到非 UI 線程便可,若是要對調用的返回作 UI 更新的話,再經過 Handler 處理便可。以下圖所示: