前言
IPC 系列文章:
建議按順序閱讀。
Android IPC 之Service 還可以這么理解
Android IPC 之Binder基礎(chǔ)
Android IPC 之Binder應(yīng)用
Android IPC 之AIDL應(yīng)用(上)
Android IPC 之AIDL應(yīng)用(下)
Android IPC 之Messenger 原理及應(yīng)用
Android IPC 之服務(wù)端回調(diào)
Android IPC 之獲取服務(wù)(IBinder)
Android Binder 原理?yè)Q個(gè)姿勢(shì)就頓悟了(圖文版)
上一篇文章分析了如何使用Binder進(jìn)行進(jìn)程間通信以及提出了直接使用Binder編碼的缺點(diǎn),本篇闡述如何使用AIDL解決以上缺點(diǎn)。
通過(guò)本篇文章,你將了解到:
1、如何編寫AIDL 文件
2、如何使用AIDL
1、如何編寫AIDL 文件
什么是AIDL
AIDL 是Android Interface Definition Language (Android 接口定義語(yǔ)言)的縮寫。
創(chuàng)建AIDL 文件
Android Studio本身支持創(chuàng)建AIDL文件,先創(chuàng)建名為IMyServer 的AIDL文件。
在Module上右鍵單擊:

輸入名字:

確定后生成 IMyServer.aidl文件:

可以看出,由于是第一次創(chuàng)建AIDL文件,因此還創(chuàng)建了aidl文件夾并添加了包名作為目錄結(jié)構(gòu),其總體結(jié)構(gòu)如下:
src/main/aidl/com/fish/myapplication/IMyServer.aidl
其中/aidl目錄與/java、/res目錄平級(jí),都在main目錄下:
app/src/main/
app/src/aidl/
app/src/res
AIDL 文件內(nèi)容
生成IMyServer.aidl內(nèi)容如下:
//包名
package com.fish.myapplication;
interface IMyServer {
//aidl 支持的基本數(shù)據(jù)類型
//默認(rèn)生成的方法,可以去掉
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
其中basicTypes(xx)方法是自動(dòng)生成的,用來(lái)指導(dǎo)我們?nèi)绾尉帉懛椒?,可以去掉?br> IMyServer 接口里聲明的方法為Server端暴露給外部調(diào)用的方法,先為Server添加方法:
//包名
package com.fish.myapplication;
interface IMyServer {
//只有一個(gè)參數(shù),并且沒(méi)有返回值
void say(String word);
//有兩個(gè)參數(shù),并且返回int
int tell(String word, int age);
}
然后編譯工程。
AIDL 編譯產(chǎn)物
編譯成功后,切換到Project模式,搜索IMyServer.java:

可以看出,編寫的AIDL文件,最終根據(jù)一定的規(guī)則映射生成Java文件,接著來(lái)看看IMyServer.java內(nèi)容。
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.fish.myapplication;
public interface IMyServer extends android.os.IInterface
{
//默認(rèn)類實(shí)現(xiàn)接口,可以不用關(guān)注
public static class Default implements com.fish.myapplication.IMyServer
{
@Override public void say(java.lang.String word) throws android.os.RemoteException
{
}
@Override public int tell(java.lang.String word, int age) throws android.os.RemoteException
{
return 0;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
public static abstract class Stub extends android.os.Binder implements com.fish.myapplication.IMyServer
{
//描述符
private static final java.lang.String DESCRIPTOR = "com.fish.myapplication.IMyServer";
public Stub()
{
//調(diào)用Binder方法,將Binder與IInterface 關(guān)聯(lián)起來(lái)
//也就是說(shuō)Binder持有IInterface引用
this.attachInterface(this, DESCRIPTOR);
}
//通過(guò)Binder找到關(guān)聯(lián)的IInterface
public static com.fish.myapplication.IMyServer asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.fish.myapplication.IMyServer))) {
//IBinder引用與調(diào)用者同一進(jìn)程,直接返回IInterface
return ((com.fish.myapplication.IMyServer)iin);
}
//不同進(jìn)程則返回Proxy,并傳入Binder
return new com.fish.myapplication.IMyServer.Stub.Proxy(obj);
}
//返回自身
@Override public android.os.IBinder asBinder()
{
return this;
}
//重寫onTransact(xx)
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
//根據(jù)code,調(diào)用不同的方法
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_say:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
//反序列化,讀取數(shù)據(jù)
_arg0 = data.readString();
this.say(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_tell:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
int _result = this.tell(_arg0, _arg1);
reply.writeNoException();
//寫入回復(fù)
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.fish.myapplication.IMyServer
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void say(java.lang.String word) throws android.os.RemoteException
{
//構(gòu)造序列化數(shù)據(jù)
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
//寫入序列化
_data.writeString(word);
//mRemote為遠(yuǎn)程的IBinder
boolean _status = mRemote.transact(Stub.TRANSACTION_say, _data, _reply, 0);
//阻塞等待transact(xx)調(diào)用結(jié)果
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().say(word);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int tell(java.lang.String word, int age) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(word);
_data.writeInt(age);
boolean _status = mRemote.transact(Stub.TRANSACTION_tell, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().tell(word, age);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.fish.myapplication.IMyServer sDefaultImpl;
}
static final int TRANSACTION_say = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_tell = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.fish.myapplication.IMyServer impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.fish.myapplication.IMyServer getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
//聲明的公共方法
public void say(java.lang.String word) throws android.os.RemoteException;
public int tell(java.lang.String word, int age) throws android.os.RemoteException;
}
代碼不長(zhǎng),但是初看起來(lái)云里霧里的,如果不怎么熟悉以上內(nèi)容,強(qiáng)烈建議先閱讀上篇文章:Android IPC 之Binder應(yīng)用,再看這篇就很容易了。
提取重點(diǎn)分析上面的代碼。
定義接口
定義了IMyServer 接口,該接口里的方法就是根據(jù)IMyServer.aidl里聲明的方法生成的。
兩個(gè)靜態(tài)類
Stub是抽象類。
繼承了Binder,重寫了onTransact(xx)方法。
實(shí)現(xiàn)了IMyServer 接口,并沒(méi)有實(shí)現(xiàn)里面的方法,這些方法待服務(wù)端實(shí)現(xiàn)。
當(dāng)onTransact(xx)被調(diào)用的時(shí)候,根據(jù)不同的code調(diào)用相應(yīng)的方法。
在上篇文章里分析的時(shí)候,onTransact(xx)與IMyServer 接口是分離的,我們需要手動(dòng)在onTransact(xx)里調(diào)用IMyServer 方法。而此時(shí)Stub將兩者結(jié)合起來(lái)了,完成了服務(wù)端與Binder驅(qū)動(dòng)的聯(lián)動(dòng)。
Proxy雖然沒(méi)有繼承自Binder,但是持有IBinder引用:mRemote。
實(shí)現(xiàn)了IMyServer,并且實(shí)現(xiàn)了其所有方法,每個(gè)方法里最終都通過(guò)mRemote調(diào)用transact(xx)完成了客戶端與Binder驅(qū)動(dòng)聯(lián)動(dòng)。
至此,通過(guò)這兩個(gè)類,分別完成了服務(wù)端、客戶端與Binder的聯(lián)動(dòng)。
繼續(xù)引用上篇的圖:

可以看出,有了AIDL自動(dòng)生成的類后:
1、繁雜的switch case 不用自己編寫了
2、序列化反序列化也不用編寫了
3、不再需要編寫transact(xx)與onTransact(xx)了
極大解放了生產(chǎn)力。
2、如何使用AIDL
既然IMyServer.java 已經(jīng)生成了,繼續(xù)來(lái)看看如何使用它。
編寫Server端業(yè)務(wù)
public class MyService extends Service {
private final String TAG = "IPC";
//構(gòu)造內(nèi)部類
private IMyServer.Stub stub = new IMyServer.Stub() {
@Override
public void say(String word) throws RemoteException {
Log.d(TAG, "receive say content:" + word + " in server");
}
@Override
public int tell(String word, int age) throws RemoteException {
Log.d(TAG, "receive tell content:" + word + " age:" + age + " in server");
return age + 1;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
//Stub 繼承自Binder,因此是IBinder類型
return stub;
}
}
首先實(shí)現(xiàn)業(yè)務(wù)接口。
其次在onBind(xx)里將Binder返回給客戶端。
業(yè)務(wù)邏輯實(shí)現(xiàn)了,等待客戶端調(diào)用。
編寫客戶端業(yè)務(wù)
先定義ServiceConnection:
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMyServer iMyServer = IMyServer.Stub.asInterface(service);
try {
iMyServer.say("how are you?");
int result = iMyServer.tell("how are you?", 18);
Log.d("IPC", "receive return content:" + result + " in client");
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
service 為IBinder引用,該引用從服務(wù)端經(jīng)過(guò)Binder驅(qū)動(dòng)傳遞而來(lái)(不一定是同一個(gè)引用)。
IMyServer.Stub.asInterface(service) 用來(lái)尋找該IBinder對(duì)應(yīng)的服務(wù)端提供的接口。
1、當(dāng)IBinder與調(diào)用者同一進(jìn)程,則IBinder為Binder類型,即為自身定義的Stub。
2、當(dāng)IBinder與調(diào)用者不是同一進(jìn)程,則IBinder為BinderProxy類型(為什么是這個(gè)類型,后續(xù)文章會(huì)分析)。
此處測(cè)試的是兩個(gè)不同的進(jìn)程,因此IBinder service指向BinderProxy。
在ServiceConnection里,當(dāng)綁定成功后調(diào)用Proxy里的方法,其內(nèi)部通過(guò)BinderProxy調(diào)用transact(xx)。
上面的邏輯都寫了,最后當(dāng)然需要綁定Service:
private void bindService() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
來(lái)看看打印結(jié)果:

可以看出通信成功了。
需要注意的是:
以上Demo都是同一個(gè)工程里編寫的,因此客戶端、服務(wù)端都能訪問(wèn)IMyServer.Stub,若是在不同的進(jìn)程,需要寫同樣的IMyServer.aidl文件。
讓Service在不同的進(jìn)程運(yùn)行只需要在AndroidManifest.xml添加如下字段:
<service android:name=".MyService" android:process=":aidl">
</service>
綁定后,運(yùn)行的兩個(gè)進(jìn)程如下:

總結(jié)AIDL用法
通過(guò)以上Demo可以看出,通過(guò)編寫AIDL實(shí)現(xiàn)IPC。
服務(wù)端僅需要兩步:
第一步
實(shí)現(xiàn)接口對(duì)應(yīng)方法的業(yè)務(wù)邏輯
第二步
在onBind(xx)里將接口關(guān)聯(lián)的Binder返回
同樣的客戶端調(diào)用服務(wù)端僅僅只需要兩步:
第一步
通過(guò)Stub拿到服務(wù)端的接口
第二步
拿到接口后調(diào)用對(duì)應(yīng)的方法

明顯的,IMyServer.java 已經(jīng)為我們實(shí)現(xiàn)了連接Binder的功能,屏蔽了對(duì)接的Binder細(xì)節(jié)??蛻舳苏{(diào)用服務(wù)端的方法(Proxy)與服務(wù)端進(jìn)行通信,就像是"直接"調(diào)用一般,符合Java一貫的面向?qū)ο蟮乃季S。
結(jié)合上一篇文章對(duì)Binder應(yīng)用的分析,以及本篇AIDL的分析,我們知道:
- AIDL 并不是我們熟知的Java、C++語(yǔ)言,而是一種規(guī)范。按此規(guī)范編寫的AIDL文件最終生成對(duì)應(yīng)的.java文件,該文件里實(shí)現(xiàn)了客戶端調(diào)用transact(xx)以及調(diào)用服務(wù)端的onTransact(xx),通過(guò).java文件就能實(shí)現(xiàn)進(jìn)程間通信。
- .java文件里將工作分為了兩部分:一是客戶端的邏輯封裝在Proxy里,而服務(wù)端的邏輯封裝在Stub里,典型的Proxy-Stub(代理-樁)模式。
- 進(jìn)程間通信的核心是Binder,AIDL本身并不能實(shí)現(xiàn)進(jìn)程間通信,僅僅是簡(jiǎn)化了編碼的流程。
接下來(lái)將重點(diǎn)分析AIDL 傳遞自定義數(shù)據(jù)類型以及定向Tag相關(guān)問(wèn)題。
本文基于Android 10.0。
您若喜歡,請(qǐng)點(diǎn)贊、關(guān)注,您的鼓勵(lì)是我前進(jìn)的動(dòng)力
持續(xù)更新中,和我一起步步為營(yíng)學(xué)習(xí)Android
1、Android各種Context的前世今生
2、Android DecorView 必知必會(huì)
3、Window/WindowManager 不可不知之事
4、View Measure/Layout/Draw 真明白了
5、Android事件分發(fā)全套服務(wù)
6、Android invalidate/postInvalidate/requestLayout 徹底厘清
7、Android Window 如何確定大小/onMeasure()多次執(zhí)行原因
8、Android事件驅(qū)動(dòng)Handler-Message-Looper解析
9、Android 鍵盤一招搞定
10、Android 各種坐標(biāo)徹底明了
11、Android Activity/Window/View 的background
12、Android Activity創(chuàng)建到View的顯示過(guò)
13、Android IPC 系列
14、Android 存儲(chǔ)系列
15、Java 并發(fā)系列不再疑惑
16、Java 線程池系列