AIDL角度分析binder

AIDL,应该说是所有用跨进程通信的最好选择,但是本身缺点也很大:

1.两个应用进程必须要包名一致

2.定义完方法后需要重新编译,特别是有parcable类型数据需要传递的时候,需要手动添加包

3.自动编译生成的java类复杂看不懂

那么为什么会有以上缺点呢?我们可不可以改进呢?答案是肯定的。

我们先来看一个最基本的aidl:

interface IRootService01 {
    //这是示例代码,可以注掉
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    //我们自己定义的一个方法,参数是String类型
    void setData(String data);
}

编译之后生成代码,可以大概看一眼就可以,实在是太麻烦了:
 

public interface IRootService01 extends android.os.IInterface
{
  /** Default implementation for IRootService01. */
  public static class Default implements com.ftabchina.aidl.service.IRootService01
  {
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
    {
    }
    @Override public void setData(java.lang.String data) throws android.os.RemoteException
    {
    }
    @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.ftabchina.aidl.service.IRootService01
  {
    private static final java.lang.String DESCRIPTOR = "com.ftabchina.aidl.service.IRootService01";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.ftabchina.aidl.service.IRootService01 interface,
     * generating a proxy if needed.
     */
    public static com.ftabchina.aidl.service.IRootService01 asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.ftabchina.aidl.service.IRootService01))) {
        return ((com.ftabchina.aidl.service.IRootService01)iin);
      }
      return new com.ftabchina.aidl.service.IRootService01.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_basicTypes:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          long _arg1;
          _arg1 = data.readLong();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          float _arg3;
          _arg3 = data.readFloat();
          double _arg4;
          _arg4 = data.readDouble();
          java.lang.String _arg5;
          _arg5 = data.readString();
          this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_setData:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.setData(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.ftabchina.aidl.service.IRootService01
    {
      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;
      }
      /**
           * Demonstrates some basic types that you can use as parameters
           * and return values in AIDL.
           */
      @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(anInt);
          _data.writeLong(aLong);
          _data.writeInt(((aBoolean)?(1):(0)));
          _data.writeFloat(aFloat);
          _data.writeDouble(aDouble);
          _data.writeString(aString);
          boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void setData(java.lang.String data) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(data);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().setData(data);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.ftabchina.aidl.service.IRootService01 sDefaultImpl;
    }
    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_setData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.ftabchina.aidl.service.IRootService01 impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.ftabchina.aidl.service.IRootService01 getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  /**
       * Demonstrates some basic types that you can use as parameters
       * and return values in AIDL.
       */
  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
  public void setData(java.lang.String data) throws android.os.RemoteException;
}

是不是看到之后头疼了?好麻烦啊,别着急,咱们先格式化一下:

public interface IRootService01 extends android.os.IInterface {
    /**
     * Default implementation for IRootService01.
     */
    public static class Default implements com.ftabchina.aidl.service.IRootService01 {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
        }

        @Override
        public void setData(java.lang.String data) throws android.os.RemoteException {
        }

        @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.ftabchina.aidl.service.IRootService01 {
        private static final java.lang.String DESCRIPTOR = "com.ftabchina.aidl.service.IRootService01";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.ftabchina.aidl.service.IRootService01 interface,
         * generating a proxy if needed.
         */
        public static com.ftabchina.aidl.service.IRootService01 asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.ftabchina.aidl.service.IRootService01))) {
                return ((com.ftabchina.aidl.service.IRootService01) iin);
            }
            return new com.ftabchina.aidl.service.IRootService01.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_basicTypes: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_setData: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.setData(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.ftabchina.aidl.service.IRootService01 {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void setData(java.lang.String data) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(data);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().setData(data);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            public static com.ftabchina.aidl.service.IRootService01 sDefaultImpl;
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_setData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

        public static boolean setDefaultImpl(com.ftabchina.aidl.service.IRootService01 impl) {
            if (Stub.Proxy.sDefaultImpl == null && impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.ftabchina.aidl.service.IRootService01 getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public void setData(java.lang.String data) throws android.os.RemoteException;
}

稍微清晰了一点,其实这些代码在自己的AS里查看会好一些,我们再把类代码整理一下,因为里面所有的类都是静态类,那么我们完全可以把代码抽取成普通类:

 最后我们可以拆成 Default类(没用,可以删掉),IRootService01接口、Proxy代理类,Stub类,其实就后面三个类有用,这三个类你仔细观察后,又可以合并成一个类:

public class RootService01 extends Binder implements IInterface {
    //常量即可
    public static final java.lang.String DESCRIPTOR = "TAG";
    //区分调用哪个方法常量
    public static final int SET_DATA = 0x01;
    //跨进程通信用binder
    private IBinder mRemote;


    //IInterface接口方法,返回自己就可以
    @Override
    public IBinder asBinder() {
        return this;
    }

    //服务端创建实例用
    public RootService01() {
        this.attachInterface(this, DESCRIPTOR);
    }

    //客户端创建实例用,但是这个构造器不对外暴露,只能通过getInstance方法获取实例
    private RootService01(IBinder binder) {
        mRemote = binder;
    }

    //客户端获取实例方法
    public static RootService01 getInstance(IBinder binder) {
        //检查是否为本地服务,也就是不是跨进程通信
        IInterface localInterface = binder.queryLocalInterface(DESCRIPTOR);
        if (localInterface instanceof RootService01) {
            return (RootService01) localInterface;
        }
        //创建远端通信对象
        return new RootService01(binder);
    }

    /**
     * 远端进程执行这个方法,由系统调用
     *
     * @param code  方法类型,区分调用的哪个方法
     * @param data  参数容器
     * @param reply 返回值容器
     * @param flags 没什么用
     * @return 表示是否执行,返回true
     * @throws RemoteException
     */
    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                if (null != reply) reply.writeString(DESCRIPTOR);
                return true;
            case SET_DATA://setData方法
                //定位到参数所在系统内存位置
                data.enforceInterface(DESCRIPTOR);
                //读取客户端传递过来的参数
                String params = data.readString();
                //调用服务端setData方法,因此服务端必须重写setData 方法,且不能调用super
                setData(params);
                //如果有返回值则
                //reply.writeString(aaa);
                //如果执行到这里,则告诉客户端代码没有执行错误
                reply.writeNoException();
                return true;

        }
        return super.onTransact(code, data, reply, flags);
    }

    /**
     * 注意这个方法在客户端和服务端都有
     * 客户端:表示向服务端发送数据,可以直接调用这个方法,输入参数
     * 服务端:必须要重写这个方法,而且不能调用super,params表示客户端传递过来的参数
     *
     * @param params
     * @throws RemoteException
     */
    public void setData(String params) throws RemoteException {
        Parcel par = Parcel.obtain();
        Parcel relay = Parcel.obtain();
        try {
            //用来标记参数在系统内存开始的地方
            par.writeInterfaceToken(DESCRIPTOR);
            //写入参数,多个参数则依次写入
            par.writeString(params);
            //通过binder将参数发送到系统共享内存处,SET_DATA表示调用哪个方法
            mRemote.transact(SET_DATA, par, relay, 0);
            //读取错误
            relay.readException();
        } finally {
            par.recycle();
            relay.recycle();
        }
    }
}

2个构造器,一个给服务端用,一个给客户端用,且客户端不可以直接通过构造器创建,4个方法:

asBinder 固定写法,返回this
getInstance 固定写法,先检查是否为同一个进程服务,客户端调用
onTransact 服务端调用方法,写法可以参考示例写,基本上也是固定写法
setData 我们要进行操作的方法,写法可以参考示例,基本上也都是固定写法

其实AIDL 本质就这么一个类,2个构造器,3个方法+我们通信的方法。

这里我们来回复一下AIDL第一个缺点,客户端服务端必须要包名一致,真的有这个必要吗?如果你按照AIDL去实现,那确实是必须要这样,如果按照我上面合成的一个类去写,那就可以随意,为什么?因为客户端和服务端需要同一个字符串类型TAG,用来做什么?用来标记参数在系统内存中开始的地方,比如客户端存到了1开始,那么服务端就可以从1开始取数据。

我们再来看看整理前使用AIDL和合并成一个类使用的区别:

整理前AIDL:(我把上面的类名改成AIDL了,要不和服务类型差不多,不好区分)

客户端:

        Intent intent=new Intent(this, RootService.class);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                IAIDL iaidl = IAIDL.Stub.asInterface(iBinder);
                Log.e("TAG", "onServiceConnected: 客户端向服务端发送数据  1234");
                try {
                    iaidl.setData("1234");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {

            }
        },Context.BIND_AUTO_CREATE);

服务端:

public class RootService extends Service {

    private IBinder mBinder = new IAIDL.Stub() {

        @Override
        public void setData(String params) throws RemoteException {
            Log.e("TAG", "setData: 客户端传递过来数据  " + params);
        }
    };

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

日志输出为:

跨进程通信成功了。下面用我们修改后的类来实现一下:

        Intent intent = new Intent(this, RootService.class);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                AIDL instance = AIDL.getInstance(iBinder);
                Log.e("TAG", "onServiceConnected: 修改后客户端发送数据为:  123456");
                try {
                    instance.setData("123456");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {

            }
        }, Context.BIND_AUTO_CREATE);
public class RootService extends Service {

    private IBinder mBinder = new AIDL() {
        @Override
        public void setData(String params) throws RemoteException {
            Log.e("TAG", "setData: 修改后客户端传递过来数据为:" + params);
        }
    };

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

日志输出为:

可以看到依旧实现了进程间通信。

我们总结一下AIDL通信的流程及注意事项:

第一步:建立连接,获取服务端Binder引用

1.调用bindservice,实际上是调用的ContextImp中bindService

2.调用AMS查找是否有这个Servcie,如果有则直接绑定返回

3.AMS通过Binder通信告诉SystemService创建Service进程,AMS通知进程启动服务

4.Service启动后通知AMS,并且将自己的onBind方法中的Binder引用告诉AMS

5.AMS拿到服务端Binder后通知客户端链接成功,并调用connected方法

第二步:开始通信

1.客户端用服务端Binder引用创建AIDL对象,如果是本地服务,则直接返回服务端AIDL对象,否则创建客户端AIDL对象,也就是服务端代理

2.将客户端数据存入Parcl中,通过服务端Binder引用,调用transact 方法,将客户端数据发送出去

3.经过Binder驱动,最终会调用到服务端onTransact方法,将客户端数据以及返回值容器去下,同时根据code码调用本地相应方法

4.Binder驱动此时处于等待状态,等待服务端返回值

5.将服务端执行结果告诉客户端

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值