Android学习笔记 AIDL使用

180 阅读3分钟

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);
        }
    };
}

通过 IPC 传递对象