Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务

系列文章
Android 进程间通信(一) – Android 多进程模式
Android 进程间通信(二) – 理解 Binder 的机制
Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务

上一章,已经学习了 Binder 的通信原理,这里再通过 AIDL 了,再来捋一遍,并自己写个 Binder。

如果你对 AIDL 不熟悉,可以参考这篇文章 AIDL使用详解及进程回调

一. AIDL 基本使用

这里也是用上面的代码,首先是任务类 TaskInfo,需要继承 Parcelable 接口,让as 帮你实现方法即可,如下:

public class TaskInfo implements Parcelable {

    public int id;
    public String url;
    public int progress;

    public TaskInfo() {
    }
    protected TaskInfo(Parcel in) {
        id = in.readInt();
        url = in.readString();
        progress = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(url);
        dest.writeInt(progress);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<TaskInfo> CREATOR = new Creator<TaskInfo>() {
        @Override
        public TaskInfo createFromParcel(Parcel in) {
            return new TaskInfo(in);
        }

        @Override
        public TaskInfo[] newArray(int size) {
            return new TaskInfo[size];
        }
    };

    @Override
    public String toString() {
        return "TaskInfo{" +
                "id=" + id +
                ", url='" + url + '\'' +
                ", progress=" + progress +
                '}';
    }
}

然后是 TaskInfo.aidl

//注意这个 TaskInfo 必须是在 com.example.ipcdemo 下,不如会提示找不到。
parcelable TaskInfo;

IRemoteService.aidl :

//记得导入 TaskInfo.aidl 的包
import com.example.ipcdemo.TaskInfo;
interface IRemoteService {
    //两数之和
    int add(int num1,int num2);

    //添加一个任务
    TaskInfo addTask(in TaskInfo info);
}

然后 build 一下,就会发现,在 build 下生成了 IRemoteService.java
在这里插入图片描述

接着看这个 IRemoteService 的 java ,代码有点长,可以略过:

public interface IRemoteService extends android.os.IInterface
{

  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.example.ipcdemo.IRemoteService
  {
    private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.ipcdemo.IRemoteService interface,
     * generating a proxy if needed.
     */
    public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {
        return ((com.example.ipcdemo.IRemoteService)iin);
      }
      return new com.example.ipcdemo.IRemoteService.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_add:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          int _arg1;
          _arg1 = data.readInt();
          int _result = this.add(_arg0, _arg1);
          reply.writeNoException();
          reply.writeInt(_result);
          return true;
        }
        case TRANSACTION_addTask:
        {
          data.enforceInterface(descriptor);
          com.example.ipcdemo.TaskInfo _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          com.example.ipcdemo.TaskInfo _result = this.addTask(_arg0);
          reply.writeNoException();
          if ((_result!=null)) {
            reply.writeInt(1);
            _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.ipcdemo.IRemoteService
    {
      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 int add(int num1, int num2) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(num1);
          _data.writeInt(num2);
          boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().add(num1, num2);
          }
          _reply.readException();
          _result = _reply.readInt();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      //添加一个任务

      @Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        com.example.ipcdemo.TaskInfo _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((info!=null)) {
            _data.writeInt(1);
            info.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_addTask, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().addTask(info);
          }
          _reply.readException();
          if ((0!=_reply.readInt())) {
            _result = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(_reply);
          }
          else {
            _result = null;
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.ipcdemo.IRemoteService sDefaultImpl;
    }
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.ipcdemo.IRemoteService impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.ipcdemo.IRemoteService getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }

  /** Default implementation for IRemoteService. */
  public static class Default implements com.example.ipcdemo.IRemoteService
  {
    //两数之和

    @Override public int add(int num1, int num2) throws android.os.RemoteException
    {
      return 0;
    }
    //添加一个任务

    @Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException
    {
      return null;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  //两数之和

  public int add(int num1, int num2) throws android.os.RemoteException;
  //添加一个任务

  public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException;

在解释之前,先了解一些概念:

  • IInterface : AIDL 文件必须继承的接口,它只有一个方法 IBinder asBinder() ,实现它的类,代表的是能够跨进程传输 Binder 对象,或者 Binder 代理对象,比如上面的代码中,返回this,表示 Stub 这个内部类是具有跨进程的作用的。
  • IBinder : 实现这个接口的对象具备跨进程书传输的能力,在跨进程数据流经驱动时,驱动会识别 Binder 类型的数据,从而自动完成不同进程 Binder 本地对象以及 Binder 代理对象的转换。

二. 分析Binder 流程

首先,我们看看我们在服务中时怎么构建 Binder 服务的:

    //AIDL 的服务,
    IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            /**
             * 这里为具体的实现方法,比如直接返回两数之和
             */
            Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);
            return (num1 + num2);
        }

        @Override
        public TaskInfo addTask(TaskInfo info) throws RemoteException {
            Log.d("zsr", "收到客户端的信息: "+info);
            info.id = 0;
            info.progress = 50;
           // mHandler.sendEmptyMessage(1);
           // mTaskInfo = info;
            return info;
        }
    };

可以看到,使用的是 IRemoteService 的子类 Stub ,那我们从这个类开始分析好了。

DESCRIPTOR
看到在 Stub 子类中,实现了一个标记字符串,用包名加类型表示:

 private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";

然后在它的构造方法中,注册当前的 Binder 和 字符串:

    public Stub() {
      this.attachInterface(this, DESCRIPTOR);
    }

后面需要从 ServerManager 中拿到binder 时,就需要这个字符串了,比如:

queryLocalInterface(DESCRIPTOR)

asInterface
将服务端的 Binder 对象转换成客户端所有的 AIDL 对象;比如:

IRemoteService mBinder = IRemoteService.Stub.asInterface(service);

这个转换是分进程的,如果同个进程,则直接返回自身即可,如果是跨进程,则需要使用它的代理类,去转换了。从代码也可以看出来:

    public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {
        return ((com.example.ipcdemo.IRemoteService)iin);
      }
      return new com.example.ipcdemo.IRemoteService.Stub.Proxy(obj);
    }

asBinder
从上面的介绍中已经知晓,返回的对象,具备跨进程的能力,这里返回 this,表示 Stub。

onTransact
该方法运行在服务端的Binder线程池中,数据的写入和结果读取都在这。

当客户端发起跨进程请求时,远程请求会通过底层封装后,交由次方法来处理;从 code 中,可以知道当前执行的是哪个方法,比如我们定义的,add() 和 addTask() 它们的 index 为:

    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

这样,我们就可以通过 code 去拿到对应的方法:
在这里插入图片描述
继续看 onTransact(int code, Parcel data, Parcel reply, int flags),它的参数有可以序列换和反序列化的 Parcel 参数,data 和 reply;
我们可以从 data 中取出参数(如果方法有参数的话),注意有顺序,然后把结构写入 replay ,这样当你的方法有返回值的时候,replay 返回的就是你需要的。
注意,onTransact 需要 返回 true,返回false 则表示请求失败。

IRemoteService#Proxy
从上面到说,当跨进程调用时,asInterface 返回的是代理类的实力,它也是实现 IRemoteService 接口,在Android 进程间通信(二) – 理解 Binder 的机制 我们知道,Binder 的代理模式,其实就是当 A 访问 B 的 object 方法,B 不会把的 object 给到 A,而是给它一个具体相同方法的代理类,然后A 通过这个代理把参数传递给 B,B 计算之后,把结果再传递给 A;从而实现跨进程的作用。

而它也确实是这么做的:
在这里插入图片描述
首先通过 _data 把参数都写进去,接着使用mRemote.transact 把参数传递给服务,它胡调用 onTransact 方法,然后通过线程池,把结果写入 reply ,最后再返回给 Binder,完成了跨进程的动作。

这样,我们就完成了 AIDL 的分析

三. 自定义 AIDL 服务

从上面看,AIDL 中,系统生成的逻辑比较乱,且所有类都集成在一起,但我们理清楚之后,还是比较容易理解的。那么这里,我们自己来写一个,而不是使用系统生成的。

首先,定义一个接口,让它集成 IInterface,并添加 add 和 addTask 方法,且定义好方法的 id:

public interface IRemote extends IInterface {
    static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.aidl.IRemote";

    static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addTask = (IBinder.FIRST_CALL_TRANSACTION + 1);

    //两数之和
    public int add(int num1, int num2) throws RemoteException;
    //添加一个任务
    public TaskInfo addTask(TaskInfo info) throws RemoteException;

}

然后创建一个具备跨进程的实现类 IRemoteLmpl ,集成 Binder 和实现 IRemote 接口:

public class IRemoteImpl extends Binder implements IRemote {
    @Override
    public int add(int num1, int num2) throws RemoteException {
        return 0;
    }

    @Override
    public TaskInfo addTask(TaskInfo info) throws RemoteException {
        return null;
    }

    @Override
    public IBinder asBinder() {
        return this;
    }
}

接着,我们应该在的构造方法中,把binder 和 字符串描述注册到服务中:

    public IRemoteImpl() {
        this.attachInterface(this,DESCRIPTOR);
    }

实现供客户端调用的服务端转换的 AIDL 对象的方法 asInterface:

    public static IRemote asInterface(IBinder obj){
        if (obj == null) {
            return null;
        }
        if (obj instanceof IRemote){
            return (IRemote) obj;
        }
        return new Proxy(obj);
    }

其中 Proxy 代理类,我们后面再写。

接着,编写 IRemote 的具体实现方法 onTransact :

    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
        //通过 code 对应的方法
        switch (code) {
            case TRANSACTION_add:
                data.enforceInterface(DESCRIPTOR);
                //接受到参数数据
                int num1 = data.readInt();
                int num2 = data.readInt();
                int result = this.add(num1, num2);
                reply.writeNoException();
                //把结果返回到返回参数中
                reply.writeInt(result);
                return true;
            case TRANSACTION_addTask:
                data.enforceInterface(DESCRIPTOR);
                TaskInfo info = null;
                if (data.readInt() != 0) {
                    //通过反序列化拿到数据
                    info = TaskInfo.CREATOR.createFromParcel(data);
                }
                TaskInfo resultInfo = this.addTask(info);
                reply.writeNoException();
                if ((resultInfo!=null)) {
                    reply.writeInt(1);
                    info.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                }
                else {
                    reply.writeInt(0);
                }
                return true;
            default:
                return super.onTransact(code, data, reply, flags);
        }
    }

无法就是一些数据的转换,没啥问题,接着,实现代理类,方便跨进程调用:

 /**
     * 跨进程的代理类
     */

    private static class Proxy implements IRemote {
        private IBinder mRemote;

        Proxy(IBinder iBinder) {
            mRemote = iBinder;
        }

        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public IBinder asBinder() {
            return mRemote;
        }

        @Override
        public int add(int num1, int num2) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();

            int reslut;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                data.writeInt(num1);
                data.writeInt(num2);
                /**
                 * 可以看到,当跨进程被调用的时候,只是把调用方的参数,给自身的方法运行,然后再把结果返回回去
                 */
                mRemote.transact(TRANSACTION_add, data, replay, 0);
                replay.readException();
                reslut = replay.readInt();
            } finally {
                data.recycle();
                replay.recycle();
            }


            return reslut;
        }

        @Override
        public TaskInfo addTask(TaskInfo info) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();
            TaskInfo result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if ((info != null)) {
                    //用数据 0,1 ,来区分是否写入成功
                    data.writeInt(1);
                    info.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addTask, data, replay, 0);

                replay.readException();
                if ((0 != replay.readInt())) {
                    result = TaskInfo.CREATOR.createFromParcel(replay);
                } else {
                    result = null;
                }
            } finally {
                data.recycle();
                replay.recycle();
            }
            return result;
        }

    }

这样,我们的代码就写完了
在这里插入图片描述
接着修改给其他调用的 RemoteService 的 AIDL 服务:

    //AIDL 的服务,
    IRemoteImpl mBinder = new IRemoteImpl() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            /**
             * 这里为具体的实现方法,比如直接返回两数之和
             */
            Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);
            return (num1 + num2);
        }

        @Override
        public TaskInfo addTask(TaskInfo info) throws RemoteException {
            Log.d("zsr", "收到客户端的信息: "+info);
            info.id = 0;
            info.progress = 50;
           // mHandler.sendEmptyMessage(1);
           // mTaskInfo = info;
            return info;
        }
    };

然后,把这两个方法复制到 客户端:

在这里插入图片描述

绑定服务端服务:

//绑定 AIDL 服务
intent.setClassName("com.example.ipcdemo","com.example.ipcdemo.service.RemoteService");
    class RemoteService implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
          //  mBinder = IRemoteService.Stub.asInterface(service);
            mBinder = IRemoteImpl.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

然后在点击事件中,调用 AIDL 方法,拿到数据:

    public void testAidl(View view) {
        try {
            if (mBinder != null) {
                TaskInfo info = new TaskInfo();
                info.url = "www.google.com";
                TaskInfo taskInfo = mBinder.addTask(info);

                int num = mBinder.add(2,3);

                Log.d(TAG, "zsr testAidl: 获取到服务端数据: "+taskInfo+" "+num);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

打印如下;
在这里插入图片描述
这样,我们不通过 AIDL 也实现了跨进程通信了。

工程代码:https://gitee.com/zhengshaorui/IpcDemo

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值