无处不在的 "AIDL"


一、开篇

我们都知道,Android 系统中的进程之间是不能共享内存的,那么两个不同的应用程序之间如何通讯?这就涉及到跨进程进程通讯的方式之一:安卓接口定义语言:AIDL( Android Interface Definition Language)。

二、AIDL 使用

既然是跨进程通信,那么就需要两个进程,接下来我们分别整出一个客户端 client,一个服务端 server,然后实现客户端从服务端获取数据的功能,从中了解 AIDL 如何使用!

2.1 AIDL 服务端

2.1.1 创建服务端

mFHOaT.png

2.1.2 创建 AIDL 文件

mFbtzj.png

IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.example.service;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    // 添加一个方法
    String getString();

}

2.1.2 生成 JAVA 文件

执行 AS –> Build –> Make Module 'app' 会在 server\build\generated\aidl_source_output_dir 路径下生成 IMyAidlInterface.java 文件。

mFqEmq.png

2.1.3 创建 MyService

package com.example.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class MyService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回 MyBind 实例
        return new MyBind();
    }

    // MyBind 继承自 IMyAidlInterface.Stub
    class MyBind extends IMyAidlInterface.Stub {
        @Override
        public String getString() throws RemoteException {
            String string = "这是一条从服务端返回的字串";
            return string;
        }
    }
}

2.2 AIDL 客户端

2.2.1 复制 AIDL 文件

Server 端的 AIDL 文件复制到 Client 端,请注意:保证路径一摸一样,并且包名需要一致

mFjGxe.png

2.2.2 修改 activity_main.xml

添加两个按钮:BIND SERVICEUNBIND SERVICE,一个 TextView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="BIND SERVICE" />

    <Button
        android:id="@+id/unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="UNBIND SERVICE" />

    <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="等待服务端返回数据..."/>

</LinearLayout>

2.2.3 修改 MainActivity.java

package com.example.aidl;

import androidx.appcompat.app.AppCompatActivity;
... ...

import com.example.service.IMyAidlInterface;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button bindService, unbindService;
    private TextView textView;
    private IMyAidlInterface myAidlInterface;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
            try {
                String string = myAidlInterface.getString();
                textView.setText(string);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            myAidlInterface = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.text_view);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);

        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bind_service:
                Intent intent = new Intent();
                Log.d("marco", "bind_service setAction()");
                intent.setComponent(new ComponentName("com.example.service", 
                                                         "com.example.service.MyService"));
                bindService(intent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_service:
                unbindService(connection);
                break;
        }
    }
}

2.3 数据交互

通过上面的代码,服务端和客户端就建立了连接,我们运行下项目,看下实际结果:

mk9OdH.png

执行 BIND SERVICE 后:

mkCDTH.png

可以发现,客户端从服务端获取到了数据,这也证明通过 AIDL 机制,两个应用(进程)之间可以进行通信。


三、AIDL 原理

当我们创建了这个接口后,系统会自动生成其对应的 Binder类,它继承了 IInterface, 内部有一个静态抽象类 Stub 和 Stub 内部的 Proxy类。其中 Stub 继承了 Binder 类,所以 AIDL 中的 Stub 即为一个 Binder 对象,在服务端实现该接口后,支持在客户端远程调用。

// IMyAidlInterface.java,对于这个文件的深入分析我们后面会详细讲解,这边只需要大概看下

package com.example.service;
// Declare any non-default types here with import statements

// IMyAidlInterface 继承了 IInterface 接口
public interface IMyAidlInterface extends android.os.IInterface
{
  /** Default implementation for IMyAidlInterface. */
  public static class Default implements com.example.service.IMyAidlInterface
  {
    @Override public java.lang.String getString() throws android.os.RemoteException
    {
      return null;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  // 静态抽象类 Stub,继承了 Binder
  public static abstract class Stub extends android.os.Binder
                                implements com.example.service.IMyAidlInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.example.service.IMyAidlInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    // 静态 asInterface 方法
    public static com.example.service.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      // queryLocalInterface() 这个方法判断是否同一个进程
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      // 同进程,直接返回继承该 Stub 的 Binder
      if (((iin!=null)&&(iin instanceof com.example.service.IMyAidlInterface))) {
        return ((com.example.service.IMyAidlInterface)iin);
      }
      // 不同进程,返回静态代理类
      return new com.example.service.IMyAidlInterface.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    // 重写 onTransact 方法
    @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_getString:
        {
          data.enforceInterface(descriptor);
          java.lang.String _result = this.getString();
          reply.writeNoException();
          reply.writeString(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    // 静态内部类 Proxy(代理类)
    private static class Proxy implements com.example.service.IMyAidlInterface
    {
      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 java.lang.String getString() 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);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getString();
          }
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.service.IMyAidlInterface sDefaultImpl;
    }
    static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    public static boolean setDefaultImpl(com.example.service.IMyAidlInterface impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.service.IMyAidlInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public java.lang.String getString() throws android.os.RemoteException;
}

所以,AIDL 定义的接口,除了是一个 接口 以外,它还是一个 Binder 对象,支持在 接口Binder 之间相互转换。

接下来,我们结合上面的 Service 案例,然后剖解 IMyAidlInterface.java 文件,深入探讨 AIDL 的实现原理。

3.1 IMyAidlInterface

我们在 Server 端定义了一个 AIDL 文件:IMyAidlInterface.aidl,并且在其中定义了一个接口:String getString();

然后我们编译后,系统会帮我们自动生成一个 IMyAidlInterface.java 文件,简化如下:

// IMyAidlInterface.java

package com.example.service;
// Declare any non-default types here with import statements

// IMyAidlInterface 继承了 IInterface 接口
public interface IMyAidlInterface extends android.os.IInterface
{
  ... ...
  // 静态抽象类 Stub,继承了 Binder
  public static abstract class Stub extends android.os.Binder
                                implements com.example.service.IMyAidlInterface
  {... ...}
  public java.lang.String getString() throws android.os.RemoteException;
}

1、该文件是一个接口文件,继承了 android.os.IInterface 接口;
2、在该接口文件中,定义了在 AIDL 中申明接口的 JAVA 形式:java.lang.String;
3、在该接口文件中,定义了一个静态的抽象类 Stub,该类继承自 Binder;

3.2 IInterface

IInterface 接口是什么,我们也分析一下:

package android.os;

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface {
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    IBinder asBinder();
}

1、IInterface 是 Binder 接口的基类。当我们定义一个新的接口,则必须从 IInterface 中继承;
2、asBinder() 用于取得与这个接口有联系的 Binder 对象;

3.3 Stub

静态抽象子类 Stub 是个重头戏,我们需要好好研究一下:

  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.example.service.IMyAidlInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.example.service.IMyAidlInterface";

    public Stub() {... ...}

    public static com.example.service.IMyAidlInterface asInterface(android.os.IBinder obj) {... ...}

    @Override public android.os.IBinder asBinder() {... ...}

    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
                                    int flags) throws android.os.RemoteException {... ...}

    private static class Proxy implements com.example.service.IMyAidlInterface {... ...}

  }

1、该类实现了 asBinder 方法,此方法可将接口转化为 Binder;
2、该类提供了 asInterface 方法,此方法可以将 Binder 转化为 IMyService 实现类;
3、该类继承了 android.os.Binder 类,重写了 Binder 类的 onTransact 方法。该方法的作用是,接收客户端发来的请求,并将请求分发至各个接口。
4、该类中自定义了一个静态类 Proxy,实现了 IMyAidlInterface;

3.4 Proxy

我们看下 Proxy 类:它作为一个代理,将接收到的请求,转发给客户端。

    private static class Proxy implements com.example.service.IMyAidlInterface
    {
      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 java.lang.String getString() throws android.os.RemoteException
      {... ...}
      public static com.example.service.IMyAidlInterface sDefaultImpl;
    }

1、该类实现了 IMyAidlInterface 继承自 IInterface 的方法 asBinder;
2、该类的构造函数,以 Binder 作为传参,保存为成员变量;
3、该类实现了 IMyAidlInterface 中定义的 getString() 方法;

3.5 getString()

我们看下 getString 方法:

      @Override public java.lang.String getString() 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);
          // 通过 transact 方法将信息传送到服务端
          boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getString();
          }
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }

3.6 流程小结

结合 Service 案例,我们看看 客户端服务端 之间是如何通信的:

    ✎ (1)客户端调用 bindService 绑定服务后,将触发 Service 的 onBind 监听方法。该方法将调用 asBinder 方法,返回一个 Binder 对象。
    ✎ (2)客户端将通过 onServiceConnected 回调函数,获取到该 Binder 对象(以传参的形式传入)。
    ✎ (3)客户端获取到 Binder 对象后,调用 stub.asInterface 方法,将其转换为 service 实现类的对象
    ✎ (4)在 asInterface 方法中,将判断 service 与当前进程,是否在同一进程中。若是,则返回 stub 本身,否则返回 stub.proxy,返回结果将作为 Service 实现类的实例。
    ✎ (5)在通过 Service 实现类的实例调用接口方法时,若为同一进程,则直接调用方法本身。若为跨进程,则调用 stub.proxy 的对应接口方法,通过 Transact 方法将信息传送到服务端。此时,客户端将挂起,等待结果返回。
    ✎ (6)服务端接收到信息,通过 onTransact() 方法,根据方法的唯一标识,将信息转发至各对应方法。
    ✎ (7)信息处理完成后,再由服务端 onTransact 返回结果。