Android進階 之程序通訊之 AIDL 的使用
AIDL 是什麼
AIDL(Android 介面定義語言) 是 Android 提供的一種程序間通訊 (IPC) 機制。
我們可以利用它定義客戶端與服務使用程序間通訊 (IPC) 進行相互通訊時都認可的程式設計介面。
在 Android 上,一個程序通常無法訪問另一個程序的記憶體。 儘管如此,程序需要將其物件分解成作業系統能夠識別的原語,並將物件編組成跨越邊界的物件。
編寫執行這一編組操作的程式碼是一項繁瑣的工作,因此 Android 會使用 AIDL 來處理。
通過這種機制,我們只需要寫好 aidl 介面檔案,編譯時系統會幫我們生成 Binder 介面。
AIDL 支援的資料型別
共 4 種:
- Java 的基本資料型別
- List 和 Map
- 元素必須是 AIDL 支援的資料型別
- Server 端具體的類裡則必須是 ArrayList 或者 HashMap
- 其他 AIDL 生成的介面
- 實現 Parcelable 的實體
AIDL 如何編寫
AIDL 的編寫主要為以下三部分:
- 建立 AIDL
Parcelable
- 服務端
onBind()
- 客戶端
ServiceConnection bindService()
AIDL 例項
下面以例項程式碼演示一個 AIDL 的編寫。
1.建立 AIDL
①建立要操作的實體類,實現 Parcelable
介面,以便序列化/反序列化
package net.sxkeji.shixinandroiddemo2.bean; import android.os.Parcel; import android.os.Parcelable; public class Person implements Parcelable { private String mName; public Person(String name) { mName = name; } protected Person(Parcel in) { mName = in.readString(); } public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mName); } @Override public String toString() { return "Person{" + "mName='" + mName + '\'' + '}'; } }
實現 Parcelable 介面是為了後序跨程序通訊時使用。
關於 Parcelable 可以看我的這篇文章 Android 進階6:兩種序列化方式 Serializable 和 Parcelable 。
注意 實體類所在的包名。
②新建 aidl 資料夾,在其中建立介面 aidl 檔案以及實體類的對映 aidl 檔案
在 main 資料夾下新建 aidl 資料夾,使用的包名要和 java 資料夾的包名一致:

image
先建立實體類的 對映 aidl 檔案 ,Person.aidl:
// Person.aidl package net.sxkeji.shixinandroiddemo2.bean; //還要和宣告的實體類在一個包裡 parcelable Person;
在其中宣告對映的實體類名稱與型別
注意,這個 Person.aidl 的包名要和實體類包名一致。
然後建立 介面 aidl 檔案 ,IMyAidl.aidl:
// IMyAidl.aidl package net.sxkeji.shixinandroiddemo2; // Declare any non-default types here with import statements import net.sxkeji.shixinandroiddemo2.bean.Person; interface IMyAidl { /** * 除了基本資料型別,其他型別的引數都需要標上方向型別:in(輸入), out(輸出), inout(輸入輸出) */ void addPerson(in Person person); List<Person> getPersonList(); }
在介面 aidl 檔案中定義將來要在跨程序進行的操作,上面的介面中定義了兩個操作:
- addPerson: 新增 Person
- getPersonList:獲取 Person 列表
需要注意的是:
- 非基本型別的資料需要匯入,比如上面的 Person,需要匯入它的全路徑。
- 這裡的 Person 我理解的是 Person.aidl,然後通過 Person.aidl 又找到真正的實體 Person 類。
- 方法引數中,除了基本資料型別,其他型別的引數都需要標上方向型別
- in(輸入), out(輸出), inout(輸入輸出)
③Make Project ,生成 Binder 的 Java 檔案
AIDL 真正的強大之處就在這裡,通過簡單的定義 aidl 介面,然後編譯,就會為我們生成複雜的 Java 檔案。
點選 Build
-> Make Project
,然後等待構建完成。
然後就會在 build/generated/source/aidl/你的 flavor/
下生成一個 Java 檔案:

image
現在我們有了跨程序 Client 和 Server 的通訊媒介,接著就可以編寫客戶端和服務端程式碼了。
我們先跑通整個過程,這個檔案的內容下篇文章介紹。
2.編寫服務端程式碼
建立 Service,在其中建立上面生成的 Binder 物件例項,實現介面定義的方法;然後在 onBind()
中返回
建立將來要執行在另一個程序的 Service,在其中實現了 AIDL 介面中定義的方法:
public class MyAidlService extends Service { private final String TAG = this.getClass().getSimpleName(); private ArrayList<Person> mPersons; /** * 建立生成的本地 Binder 物件,實現 AIDL 制定的方法 */ private IBinder mIBinder = new IMyAidl.Stub() { @Override public void addPerson(Person person) throws RemoteException { mPersons.add(person); } @Override public List<Person> getPersonList() throws RemoteException { return mPersons; } }; /** * 客戶端與服務端繫結時的回撥,返回 mIBinder 後客戶端就可以通過它遠端呼叫服務端的方法,即實現了通訊 * @param intent * @return */ @Nullable @Override public IBinder onBind(Intent intent) { mPersons = new ArrayList<>(); LogUtils.d(TAG, "MyAidlService onBind"); return mIBinder; } }
上面的程式碼中,建立的物件是一個 IMyAidl.Stub()
,它是一個 Binder,具體為什麼是它我們下篇文章介紹。
別忘記在 Manifest 檔案中宣告:
<service android:name="net.sxkeji.shixinandroiddemo2.service.MyAidlService" android:enabled="true" android:exported="true" android:process=":aidl"/>
服務端實現了介面,在 onBind()
中返回這個 Binder,客戶端拿到就可以操作資料了。
3.編寫客戶端程式碼
這裡我們以一個 Activity 為客戶端。
①實現 ServiceConnection
介面,在其中拿到 AIDL 類
private IMyAidl mAidl; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //連線後拿到 Binder,轉換成 AIDL,在不同程序會返回個代理 mAidl = IMyAidl.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mAidl = null; } };
在 Activity 中建立一個服務連線物件,在其中呼叫 IMyAidl.Stub.asInterface()
方法將 Binder 轉為 AIDL 類。
②接著繫結服務
Intent intent1 = new Intent(getApplicationContext(), MyAidlService.class); bindService(intent1, mConnection, BIND_AUTO_CREATE);
要執行 IPC,必須使用 bindService()
將應用繫結到服務上。
注意:
5.0 以後要求顯式呼叫 Service,所以我們無法通過 action 或者 filter 的形式呼叫 Service,具體內容可以看這篇文章 Android 進階:Service 的一些細節 。
③拿到 AIDL 類後,就可以呼叫 AIDL 類中定義好的操作,進行跨程序請求
@OnClick(R.id.btn_add_person) public void addPerson() { Random random = new Random(); Person person = new Person("shixin" + random.nextInt(10)); try { mAidl.addPerson(person); List<Person> personList = mAidl.getPersonList(); mTvResult.setText(personList.toString()); } catch (RemoteException e) { e.printStackTrace(); } }
執行結果
[圖片上傳中...(image-1f7858-1548074843359-0)]
可以看到,Activity 與 另外一個程序的 Service 通訊成功了。
總結
這篇文章介紹了 AIDL 的簡單編寫流程,其中也踩過一些坑,比如檔案所在包的路徑不統一,繫結服務收不到回撥等問題。
到最後雖然跨程序通訊成功,但是我們還是有很多疑問的,比如:
- AIDL 生成的檔案內容?
- 什麼是 Binder?
- 為什麼要這麼寫?
知其然還要知其所以然,這一切都要從 Binder 講起,且聽下一回合介紹。
程式碼地址
喜歡的話請幫忙轉發一下能讓更多有需要的人看到吧,有些技術上的問題大家可以多探討一下。

