AIDL (Android Interface Definition Language)即Android接口定义语言。若需多线程同时处理其他应用进程的请求才有必要使用AIDL。如果不需多线程交互,则可使用Messenger(请看《Android进程间通信(一):Messenger》);而如果不需跨进程通信,使用Binder即可。
AIDL接口通常与Service联合使用。客户端通过bindService()与服务器建立连接。每当客户端调用服务器的AIDL接口时,系统就会从服务器进程的线程池中派发一个新线程进行处理,因此AIDL接口的实现必须做到线程安全。注意检查客户端传过来的参数是否合法。服务器端抛出的异常不会发回给客户端。
1、创建.aidl文件
在Android Studio中创建.aidl文件:
点击Android Studio最左侧的Project竖选项卡,
然后在其展栏的左上角点选Project,
接着在app/src/main/aidl/目录(若aidl目录不存在则新建)上右键,
New > AIDL > AIDL File,
在最后打开的对话框输入.aidl文件名并按Finish按钮即可。
.aidl文件路径示例:app/src/main/aidl/*com/company/app*/IRemoteService.aidl。
创建或修改好文件后,点击Android Studio顶部工具栏的
Tools > Android > Sync Project with Gradle Files,
就会在app/build/generated/source/aidl/debug/目录下
生成或更新*com/company/app*/IRemoteService.java文件。
如果客户端与服务器处在不同的应用中,那么客户端也需要拷贝一份.aidl文件以便生成相应的.java文件。
.aidl文件中只能定义一个接口及其若干个方法。
AIDL默认支持的数据类型:
Java语言的基本类型,如int、long、char、boolean、String、CharSequence等等;
还有List 、Map。
List、Map的元素数据类型为上述列表中的类型,或者其他AIDL生成的接口,或者可打包(parcelable)类型。List可以当泛型类使用(例如List),而Map则不支持。
其他类型需要import(具体请看下文的第二节:在进程间传递对象)。
所有非基本类型在作为参数时需要有一个方向标签(in、out、inout)来表明数据的流向。跨进程传递参数的过程包括数据序列化、传输、接收和反序列化等。方向标签有助于系统优化数据传输过程,提高效率。
———— in ———— 》
远程客户端 本地服务器
《———— out ————
// inout是最耗费资源的:系统需要先把客户端的实参对象序列化,
// 传到服务器,然后反序列化成形参对象供服务器使用。
// 接口方法调用完返回时,系统又需要把服务器的形参对象序列化,
// 传到客户端,再反序列化后修改客户端的实参对象。
// 若是在同一个进程中,修改形参对象就能直接影响实参对象,
// 就像C/C++中的指针那样。
void charsToUpper(inout char[] lowerChars);
AIDL有一个关键字oneway,远程客户端调用其修饰的方法时,不阻塞等待结果而是立即返回;本地调用则是同步等待(如同未修饰一样)。它所修饰的方法返回值必须是void,否则生成的.java文件会报错。
// IRemoteService.aidl
package com.company.app;
// 这个oneway修饰接口中所有方法
oneway interface IRemoteService{
// 这个oneway只修饰此方法
oneway void doSomething();
}
2、实现接口
自动生成的IRemoteService.java文件有一个抽象子类:Stub,它继承了Binder类并实现了.aidl文件中的接口。
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public void doSomething() {
// Do something here
}
};
3、暴露接口给客户端
// 服务器的Service类
public class RemoteService extends Service {
// 当客户端调用bindService()时,系统调用此方法。
// 客户端调用的详情请看下文的第三节:跨进程调用一个方法。
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public void doSomething() {
// Do something here
}
};
...
}
如果需要跨进程传递一个对象,那么它的类必须做到以下几点,使得Android在跨进程传递数据时可以分解(decompose)、重组(marshalled)它:
1、实现Parcelable接口;
2、实现writeToParcel()方法,它负责把类的状态保存到Parcel中;
3、在类内创建一个名为CREATOR的静态成员,它是一个实现Parcelable.Creator接口的对象;
4、创建一个.aidl文件来声明这个可打包(parcelable)的类。
以下为Android的Rect类示例:
// Rect.aidl
package android.graphics;
// 定义Rect类,以便AIDL能找到它并知道它实现了可打包协议。
parcelable Rect;
此时.java文件需要自己实现:
// Rect.java
package android.graphics;
import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator<Rect> CREATOR = new
Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public Rect() {
}
private Rect(Parcel in) {
readFromParcel(in);
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
...
}
然后就可以在.aidl文件中使用这个类:
```
// IAnotherRemoteService.aidl
package com.company.app;
import android.graphics.Rect;
interface IAnotherRemoteService{
void setRect(in Rect rect);
}
1、包含.aidl文件到上述路径(请看上文第一节《定义AIDL接口》的第1小节《创建.aidl文件》);
2、定义一个IBinder接口的实例;
3、实现ServiceConnection;
4、调用Context.bindService(),传递参数ServiceConnection;
5、在ServiceConnection.onServiceConnected()的实现中,会有一个IBinder接口参数名为service,调用YourInterfaceName.Stub.asInterface((IBinder)service)来把它转换成之前定义的AIDL接口类型。
6、调用在AIDL接口中声明的方法。需要注意连接出错时抛出的DeadObjectException,它是调用远程方法时可能出现的唯一异常。
7、调用Context.unbindService()来断开连接。
对象作为跨进程方法参数时,其引用计数会增加。匿名对象可以作为参数。
package com.company.app;
import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.ComponentName;
import android.os.IBinder;
import android.util.Log;
// 客户端的Activity
public class ClientActivity extends Activity {
private final String LOG_TAG = "ClientActivity";
private IRemoteService mIRemoteService;
private boolean mIsBound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, IRemoteService.class);
// 调用此方法与服务器连接
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(mServiceConnection);
mIsBound = false;
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// asInterface()是在Stub中自动生成的helper方法
mIRemoteService = IRemoteService.Stub.asInterface(service);
mIsBound = true;
}
public void onServiceDisconnected(ComponentName className) {
Log.e(LOG_TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
mIsBound = false;
}
};
// 在适当的时机调用远程服务器接口。
// 注意应当避免在Activity主线程中调用。
// mIRemoteService.doSomething();
...
}
参考资料:
Android > Develop > API Guides > Android Interface Definition Language (AIDL)
https://developer.android.com/intl/zh-cn/guide/components/aidl.html#PassingObjects