四、AIDL(一)同一应用中使用AIDL及原理

一、Service 开启和停止
二、Service 执行耗时工作
三、IPC常用方式
四、AIDL(一)同一应用中使用AIDL及原理
五、AIDL(二)不同应用中使用、自定义数据类型及定向Tag
六、AIDL(三)实现回调
七、AIDL(四)获取服务及绑定和Binder传递流程


四、AIDL(一)同一应用中使用AIDL及原理

4.1 AIDL 概述

借助Binder可以实现跨进程的通信,但是,在使用的过程中,我们可以发现:

  1. 编写冗余:在onTransact方法中,面对不同运算要求,使用switchcode进行区分,同样的,在客户端也要对code进行区分;
  2. 重复序列化数据:在客户端发送数据前,先将数据序列化,在服务端接收数据处理前,先将数据反序列化。这个过程也是个重复的体力活,实际上双方都不关心具体的序列化细节,只知道丢个参数进去,弄个参数出来即可;
  3. 面向对象:客户端先要调用transact(xx),服务端在onTransact(xx)里调用对应的操作方法。 这样看起来有点绕,实际上比较好的方式是客户端直接调用服务端的操作方法

上面的缺点,都可以使用AIDL ( Android Interface Definition Language )克服,但是并不是任何场景都推荐使用AIDL,只有在需要不同应用的客户端通过IPC方式访问服务,并且希望在服务中进行多线程处理时,您才有必要使用AIDL。如果您无需跨不同应用执行并发IPC,则应通过[实现 Binder 来创建接口;或者,如果您想执行 IPC,但需要处理多线程,请使用Messenger来实现接口。

4.2 AIDL 语法

AIDL基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好。跟Java相比,主要有下面这些点:

  • 文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java

  • 数据类型AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下——在 Java 中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做 Book.java,另一个叫做 BookManager.aidl,它们都在 com.ieening.aidldemo 包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上 import com.ieening.aidldemo.Book;哪怕 .java 文件和 .aidl 文件就在一个包下。默认支持的数据类型包括:

    • Java中的八种基本数据类型,包括 byteshortintlongfloatdoublebooleanchar
    • String类型。
    • CharSequence类型。
    • List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelableList可以使用泛型。
    • Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelableMap是不支持泛型的。
  • 定向tag:定向 tag 是这样的:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。Java 中的基本类型和 StringCharSequence 的定向 tag 默认且只能是 in

  • 两种AIDL文件:所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“接口定义语言”。

    第一类AIDL文件:

    // Book.aidl
    // 第一类 AIDL 文件的例子
    // 这个文件的作用是引入了一个序列化对象 Book 供其他的 AIDL 文件使用
    //注意:Book.aidl 与 Book.java 的包名应当是一样的
    package com.ieening.ipcclient;
    
    //注意parcelable是小写
    parcelable Book;
    
    
    

    第二类AIDL文件:

    // BookManager.aidl
    // 第二类 AIDL 文件的例子
    package com.ieening.ipcclient;
    // 导入所需要使用的非默认支持数据类型的包
    import com.ieening.ipcclient.Book;
    
    interface BookManager {
        // 所有的返回值前都不需要加任何东西,不管是什么数据类型
        List<Book> getBooks();
        Book getBook();
        int getBookCount();
    
        // 传参时除了Java 基本类型以及 String,CharSequence 之外的类型,都需要在前面加上定向 tag ,具体加什么量需而定,而不是全部使用 inout ,等参数变多时,维护参数的开销就会很庞大
        void setBookPrice(in Book book , int price)
        void setBookName(in Book book , String name)
        void addBookIn(in Book book);
        void addBookOut(out Book book);
        void addBookInout(inout Book book);
    }
    

4.3 同一 应用下使用 AIDL

测试代码 – AndroidIPC

新建ICalculatorAidlInterface.aidl文件,定义完文件后,需要重新编译处理,编译后在项目的gen目录下会生成一个ICalculatorAidlInterface.java文件,暂时不贴这个文件的代码了,后面会详细说明。

// ICalculatorAidlInterface.aidl
package com.ieening.server;

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

interface ICalculatorAidlInterface {
    float twoNumberAdd(float firstNumber, float secondNumber);
    
    float twoNumberSubtract(float firstNumber, float secondNumber);
    
    float twoNumberMultiply(float firstNumber, float secondNumber);
    
    float twoNumberDivide(float firstNumber, float secondNumber);
}

新建服务端Service,服务端Service比较简单,在onBind函数中返回了一个Binder

package com.ieening.server.services;

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

import androidx.annotation.Nullable;

import com.ieening.server.ICalculatorAidlInterface;


public class CalculatorAidlService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return calculatorAidlBinder;
    }

    private final ICalculatorAidlInterface.Stub calculatorAidlBinder = new ICalculatorAidlInterface.Stub() {
        @Override
        public float twoNumberAdd(float firstNumber, float secondNumber) throws RemoteException {
            return firstNumber + secondNumber;
        }

        @Override
        public float twoNumberSubtract(float firstNumber, float secondNumber) throws RemoteException {
            return firstNumber - secondNumber;
        }

        @Override
        public float twoNumberMultiply(float firstNumber, float secondNumber) throws RemoteException {
            return firstNumber * secondNumber;
        }

        @Override
        public float twoNumberDivide(float firstNumber, float secondNumber) throws RemoteException {
            return firstNumber / secondNumber;
        }
    };

}

客户端主要代码如下。客户端主要完成两件事,一:绑定和解绑Service,二是与Service通信。绑定和解绑和以前一样,使用ServiceConnectionbindServiceunBindService函数;通信使用Binder,调用相关逻辑获取结果。

public class MainActivity extends AppCompatActivity {
    ......
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        calculatorAidlServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                iCalculatorAidlBinder = ICalculatorAidlInterface.Stub.asInterface(service);
                Log.d(TAG, "executing calculatorAidlServiceConnection onServiceConnected");
                changeCalculatorCalculateButtonStatus();
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                iCalculatorAidlBinder = null;
                Log.d(TAG, "executing calculatorAidlServiceConnection onServiceDisconnected");
                changeCalculatorCalculateButtonStatus();
            }
        };
        ......
        setCalculatorAidlServiceBindUnbindButtonOnClickListener(calculatorAidlServiceConnection);
        ......
    }
    ......
    private void setCalculatorAidlServiceBindUnbindButtonOnClickListener(ServiceConnection calculatorAidlServiceConnection) {
        binding.bindCalculatorAidlServiceButton.setOnClickListener(v -> {
            Intent bindIntent = new Intent(getApplicationContext(), CalculatorAidlService.class);
            bindIntent.setAction("com.ieening.server.services.action.CalculatorAidlService");
            Log.d(TAG, "click bind calculator aidl service button to bind service");
            boolean bindResult = bindService(bindIntent, calculatorAidlServiceConnection, Context.BIND_AUTO_CREATE);
            if (bindResult) {
                Toast.makeText(this, "bind service CalculatorAidlService successes", Toast.LENGTH_SHORT).show();
                binding.unbindCalculatorAidlServiceButton.setEnabled(true);
                binding.bindCalculatorAidlServiceButton.setEnabled(false);
            } else {
                Toast.makeText(this, "bind service CalculatorAidlService failed", Toast.LENGTH_SHORT).show();
            }

            changeCalculatorCalculateButtonStatus();
        });
        binding.unbindCalculatorAidlServiceButton.setOnClickListener(v -> {
            unbindService(calculatorAidlServiceConnection);

            binding.unbindCalculatorAidlServiceButton.setEnabled(false);
            binding.bindCalculatorAidlServiceButton.setEnabled(true);

            iCalculatorAidlBinder = null;

            changeCalculatorCalculateButtonStatus();
        });
    }
    ......
    private void setOperationButtonsOnClickListener() {
        binding.addButton.setOnClickListener(v -> {
            ......

            if (Objects.isNull(iCalculatorAidlBinder)) {
                Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
            } else {
                try {
                    float result = iCalculatorAidlBinder.twoNumberAdd(firstNumber, secondNumber);
                    binding.calculatorResultTextView.setText(Float.toString(result));
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        binding.subtractButton.setOnClickListener(v -> {
            ......
            if (Objects.isNull(iCalculatorAidlBinder)) {
                Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
            } else {
                try {
                    float result = iCalculatorAidlBinder.twoNumberSubtract(firstNumber, secondNumber);
                    binding.calculatorResultTextView.setText(Float.toString(result));
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        binding.multiplyButton.setOnClickListener(v -> {
            ......
            if (Objects.isNull(iCalculatorAidlBinder)) {
                Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
            } else {
                try {
                    float result = iCalculatorAidlBinder.twoNumberMultiply(firstNumber, secondNumber);
                    binding.calculatorResultTextView.setText(Float.toString(result));
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        binding.divideButton.setOnClickListener(v -> {
            ......
            if (Objects.isNull(iCalculatorAidlBinder)) {
                Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
            } else {
                try {
                    float result = iCalculatorAidlBinder.twoNumberDivide(firstNumber, secondNumber);
                    binding.calculatorResultTextView.setText(Float.toString(result));
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
    ......
}

结果展示:

Aidl Service Demo

4.4 分析原理

首先来看ICalculatorAidlInterface.aidl生成的代码。

  1. 注释1:ICalculatorAidlInterface继承了android.os.IInterface接口,该接口是Binder接口的基类,里面定义了一个方法public IBinder asBinder()
  2. 注释2:定义了Default静态类,实现了我们在ICalculatorAidlInterface.aidl文件中定义的四个方法;
  3. 注释3:接下来是继承了Binder并实现了ICalculatorAidlInterface的抽象类Stub
    1. 注释4:asInterface静态方法是供Client端使用的,客户端在bindService()时会获取到一个服务的IBinder对象,然后通过此方法就可以获取到一个代表服务的引用(有可能是服务的直接引用,也可能是服务的代理对象);
    2. 注释5:BinderonTransact方法,解析来自客户端的调用请求,然后调用实现了的逻辑方法;
    3. 注释6:Proxy实现了ICalculatorAidlInterface接口,重点是,里面重写了ICalculatorAidlInterface.aidl中定义的接口方法;
/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.ieening.server;
// Declare any non-default types here with import statements
public interface ICalculatorAidlInterface extends android.os.IInterface // 注释 1
{
  /** Default implementation for ICalculatorAidlInterface. */
  public static class Default implements com.ieening.server.ICalculatorAidlInterface // 注释 2
  {
    @Override public float twoNumberAdd(float firstNumber, float secondNumber) throws android.os.RemoteException
    {
      return 0.0f;
    }
    @Override public float twoNumberSubtract(float firstNumber, float secondNumber) throws android.os.RemoteException
    {
      return 0.0f;
    }
    @Override public float twoNumberMultiply(float firstNumber, float secondNumber) throws android.os.RemoteException
    {
      return 0.0f;
    }
    @Override public float twoNumberDivide(float firstNumber, float secondNumber) throws android.os.RemoteException
    {
      return 0.0f;
    }
    @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.ieening.server.ICalculatorAidlInterface // 注释 3
  {
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.ieening.server.ICalculatorAidlInterface interface,
     * generating a proxy if needed.
     */
    public static com.ieening.server.ICalculatorAidlInterface asInterface(android.os.IBinder obj) // 注释 4
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.ieening.server.ICalculatorAidlInterface))) { // obj 和 ICalculatorAidlInterface同进程
        return ((com.ieening.server.ICalculatorAidlInterface)iin);
      }
      return new com.ieening.server.ICalculatorAidlInterface.Stub.Proxy(obj); // obj 和 ICalculatorAidlInterface 不同进程,IBinder 参数包装成一个 Proxy 对象
    }
    @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 // 注释 5
    {
      java.lang.String descriptor = DESCRIPTOR;
      if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
        data.enforceInterface(descriptor);
      }
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
      }
      switch (code)
      {
        case TRANSACTION_twoNumberAdd:
        {
          float _arg0;
          _arg0 = data.readFloat();
          float _arg1;
          _arg1 = data.readFloat();
          float _result = this.twoNumberAdd(_arg0, _arg1);
          reply.writeNoException();
          reply.writeFloat(_result);
          break;
        }
        case TRANSACTION_twoNumberSubtract:
        {
          float _arg0;
          _arg0 = data.readFloat();
          float _arg1;
          _arg1 = data.readFloat();
          float _result = this.twoNumberSubtract(_arg0, _arg1);
          reply.writeNoException();
          reply.writeFloat(_result);
          break;
        }
        case TRANSACTION_twoNumberMultiply:
        {
          float _arg0;
          _arg0 = data.readFloat();
          float _arg1;
          _arg1 = data.readFloat();
          float _result = this.twoNumberMultiply(_arg0, _arg1);
          reply.writeNoException();
          reply.writeFloat(_result);
          break;
        }
        case TRANSACTION_twoNumberDivide:
        {
          float _arg0;
          _arg0 = data.readFloat();
          float _arg1;
          _arg1 = data.readFloat();
          float _result = this.twoNumberDivide(_arg0, _arg1);
          reply.writeNoException();
          reply.writeFloat(_result);
          break;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
      return true;
    }
    private static class Proxy implements com.ieening.server.ICalculatorAidlInterface
    {
      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 float twoNumberAdd(float firstNumber, float secondNumber) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        float _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeFloat(firstNumber);
          _data.writeFloat(secondNumber);
          boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberAdd, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readFloat();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public float twoNumberSubtract(float firstNumber, float secondNumber) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        float _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeFloat(firstNumber);
          _data.writeFloat(secondNumber);
          boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberSubtract, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readFloat();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public float twoNumberMultiply(float firstNumber, float secondNumber) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        float _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeFloat(firstNumber);
          _data.writeFloat(secondNumber);
          boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberMultiply, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readFloat();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public float twoNumberDivide(float firstNumber, float secondNumber) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        float _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeFloat(firstNumber);
          _data.writeFloat(secondNumber);
          boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberDivide, _data, _reply, 0);
          _reply.readException();
          _result = _reply.readFloat();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
    }
    static final int TRANSACTION_twoNumberAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_twoNumberSubtract = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_twoNumberMultiply = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    static final int TRANSACTION_twoNumberDivide = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
  }
  // 服务的 Binder 标示符,在 RPC 调用服务方式时就是通过这个标示符来找到我们实现的服务
  public static final java.lang.String DESCRIPTOR = "com.ieening.server.ICalculatorAidlInterface";
  public float twoNumberAdd(float firstNumber, float secondNumber) throws android.os.RemoteException;
  public float twoNumberSubtract(float firstNumber, float secondNumber) throws android.os.RemoteException;
  public float twoNumberMultiply(float firstNumber, float secondNumber) throws android.os.RemoteException;
  public float twoNumberDivide(float firstNumber, float secondNumber) throws android.os.RemoteException;
}

结合ICalculatorAidlInterface、服务端CalculatorAidlService.java和客户端MainActivity.java,整理出下面的UML类图:

aidl UML 类图

整个通信的流程如下:

Aidl Binder 通信流程

  1. 首先,ServerSM容器中注册。
  2. 其次,Client若要调用Server的方法,就需要先获取Server对象,但是SM不会把真正的Server对象返回给Client,而是把Server的一个代理对象,也就是Proxy,返回给Client。□
  3. 再次,Client调用Proxy的方法,BinderServiceManager通过transactonTransact会帮它在Service中调用对应的方法,并把结果返回给Client

测试代码 – AndroidIPC

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值