AIDL (Android 接口定义语言)
您可以利用它定义客户端与服务均认可的编程接口,以便二者使用进程间通信 (IPC) 进行相互通信。
在 Android 中,一个进程通常无法访问另一个进程的内存。因此,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。 编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL 为您处理此问题。
注意:只有在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理时,您才有必要使用 AIDL。如果您无需跨不同应用执行并发 IPC,则应通过实现 Binder 来创建接口;或者,如果您想执行 IPC,但不需要处理多线程,请使用 Messenger 来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。
定义 AIDL 接口
1. 创建 .aidl 文件
默认情况下,AIDL 支持下列数据类型
- Java 编程语言中的所有原语类型(如 int、long、char、boolean float double short byte 等)
- String
- CharSequence
- List
- 子元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型
- 尽管生成的方法旨在使用 List 接口,但另一方实际接收的具体类始终是 ArrayList
- Map
- 不支持泛型 Map(如 Map<String,Integer> 形式的 Map)
- 尽管生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap
package com.example.demo2;
interface IHelloService{
// say hello to you
String sayHello(String name);
}
即使您在与接口相同的包内定义上方未列出的附加类型,亦须为其各自加入一条 import 语句
2. 实现接口
当您构建应用时,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件。生成的接口包含一个名为 Stub 的子类(例如,YourInterface.Stub),该子类是其父接口的抽象实现,并且会声明 .aidl 文件中的所有方法。
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.demo2;
public interface IHelloService extends android.os.IInterface
{
/** Default implementation for IHelloService. */
public static class Default implements com.example.demo2.IHelloService
{
// say hello to you
@Override public java.lang.String sayHello(java.lang.String name) throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.demo2.IHelloService
{
private static final java.lang.String DESCRIPTOR = "com.example.demo2.IHelloService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.demo2.IHelloService interface,
* generating a proxy if needed.
*/
public static com.example.demo2.IHelloService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.demo2.IHelloService))) {
return ((com.example.demo2.IHelloService)iin);
}
return new com.example.demo2.IHelloService.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_sayHello:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.sayHello(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.demo2.IHelloService
{
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;
}
// say hello to you
@Override public java.lang.String sayHello(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().sayHello(name);
}
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.demo2.IHelloService sDefaultImpl;
}
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.demo2.IHelloService impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.demo2.IHelloService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
// say hello to you
public java.lang.String sayHello(java.lang.String name) throws android.os.RemoteException;
}
如要实现 .aidl 生成的接口,请扩展生成的 Binder 接口(例如,YourInterface.Stub),并实现继承自 .aidl 文件的方法。
private final IHelloService.Stub binder = new IHelloService.Stub() {
@Override
public String sayHello(String name) throws RemoteException {
return String.format("Hello %s", name);
}
};
3. 向客户端公开接口
在为服务实现接口后,您需要向客户端公开该接口,以便客户端进行绑定。如要为您的服务公开该接口,请扩展 Service 并实现 onBind(),从而返回实现生成的 Stub 的类实例(如前文所述)。以下是向客户端公开 IRemoteService 示例接口的服务示例。
package com.example.demo2.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import com.example.demo2.IHelloService;
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return binder;
}
private final IHelloService.Stub binder = new IHelloService.Stub() {
@Override
public String sayHello(String name) throws RemoteException {
return String.format("Hello %s", name);
}
};
}