美文网首页
AIDL之定向Tag

AIDL之定向Tag

作者: 风月寒 | 来源:发表于2021-01-19 17:49 被阅读0次
    支持的数据类型

    1、八种基本数据类型:byte、char、short、int、long、float、double、boolean

    2、String,CharSequence

    3、实现了Parcelable接口的数据类型

    4、List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象

    5、Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象

    定向Tag

    定向Tag。定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。

    其中 in 表示数据只能由客户端流向服务端,

    out 表示数据只能由服务端流向客户端,

    而 inout 则表示数据可在服务端与客户端之间双向流通。

    此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。定向Tag具体的使用差别后边会有介绍

    具体使用

    https://github.com/fengyuehan/Test/tree/master/service

    下面重点讲定向Tag。

    首先在aidl文件中创建几个方法,

    interface IRemoteBookBinder {
        void addBookIn(in BookBean bookBean);
    
        void addBookOut(out BookBean bookBean);
    
        void addBookInOut(inout BookBean bookBean);
    
        List<BookBean> getBookList();
    }
    

    要了解其通信原理,得先了解几个概念:

    IBinder:代表跨进程传输的能力。只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。

    IInterface:IBinder负责数据传输,而client与server调用的契机就是继承了IInterface的aidl里面的类。我们写好aidl文件之后,build成功之后会生成一个java文件。然后通过stub和proxy去实现该接口。这样客户端就可以调用服务端的方法。

    public interface IRemoteBookBinder extends android.os.IInterface
    {
      /** Default implementation for IRemoteBookBinder. */
      public static class Default implements com.example.service.IRemoteBookBinder
      {
        @Override public void addBookIn(com.example.service.bean.BookBean bookBean) throws android.os.RemoteException
        {
        }
        @Override public void addBookOut(com.example.service.bean.BookBean bookBean) throws android.os.RemoteException
        {
        }
        @Override public void addBookInOut(com.example.service.bean.BookBean bookBean) throws android.os.RemoteException
        {
        }
        @Override public java.util.List<com.example.service.bean.BookBean> getBookList() throws android.os.RemoteException
        {
          return null;
        }
        @Override
        public android.os.IBinder asBinder() {
          return null;
        }
      }
    

    Stub:binder的本地类

    Proxy:binder的代理类。

    Stub和Proxy在客户端和服务端同时都存在,而不是Proxy存在客户端,stub存在服务端。一般是从Proxy开始请求,Stub去接收处理请求。

    在服务端我们RemoteBookBinderImpl去继承Stub,表明RemoteBookBinderImpl实例具有代表跨进程传输的能力。然后通过onBind方法返回。

    public class RemoteBookBinderImpl extends IRemoteBookBinder.Stub{
            private List<BookBean> bookBeans = new ArrayList<>();
            @Override
            public void addBookIn(BookBean bookBean) throws RemoteException {
                Logger.i("------------------------服务端修改前的数据----------------------");
                Logger.i("" + bookBean.toString() + currentProgressAndThread());
                Logger.i("--------------------------------------------------------------");
                bookBean.bookAuthor = "zzf";
                bookBeans.add(bookBean);
                Logger.i("------------------------服务端修改后的数据----------------------");
                Logger.i("" + bookBean.toString() + currentProgressAndThread());
                Logger.i("--------------------------------------------------------------");
            }
    
            @Override
            public void addBookOut(BookBean bookBean) throws RemoteException {
                Logger.i("------------------------服务端修改前的数据----------------------");
                Logger.i("" + bookBean.toString() + currentProgressAndThread());
                Logger.i("--------------------------------------------------------------");
                bookBean.bookAuthor = "zzf";
                bookBeans.add(bookBean);
                Logger.i("------------------------服务端修改后的数据----------------------");
                Logger.i("" + bookBean.toString() + currentProgressAndThread());
                Logger.i("--------------------------------------------------------------");
            }
    
            @Override
            public void addBookInOut(BookBean bookBean) throws RemoteException {
                Logger.i("------------------------服务端修改前的数据----------------------");
                Logger.i("" + bookBean.toString() + currentProgressAndThread());
                Logger.i("--------------------------------------------------------------");
                bookBean.bookAuthor = "zzf";
                bookBeans.add(bookBean);
                Logger.i("------------------------服务端修改后的数据----------------------");
                Logger.i("" + bookBean.toString() + currentProgressAndThread());
                Logger.i("--------------------------------------------------------------");
            }
    
            @Override
            public List<BookBean> getBookList() throws RemoteException {
                if (ListUtils.notEmpty(bookBeans)) {
                    Logger.i("-------- 书本总数: " + bookBeans.size() + " ------------");
                    for (BookBean bookBean : bookBeans) {
                        Logger.i("BookList => " + bookBean);
                    }
                    Logger.i("-----------------------------------");
                }
                return bookBeans;
            }
        }
        
    public IBinder onBind(Intent intent) {
            remoteBookBinder = new RemoteBookBinderImpl();
            return remoteBookBinder;
        }
    

    此时需要在客户端获取到这个实例,这样就建立起连接。

    当我们在客户端bindService的时候,会传入bindConnection参数,当连接成功的时候,会将service通过回调返回过来,然后通过调用asInterface()去判断。

    bindService(intent, bindConnection, Service.BIND_AUTO_CREATE);
    
    private ServiceConnection bindConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                iRemoteBookBinder = IRemoteBookBinder.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                iRemoteBookBinder = null;
            }
        };
    

    下面看下asInterface():

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

    如果是同一个进程,则返回当前对象,如果不是同一个进程,则放回去代理对象。

    Proxy(android.os.IBinder remote)
       {
            mRemote = remote;
        }
    @Override public android.os.IBinder asBinder()
    {
        return mRemote;
    }
    

    在这里通过静态代理的方式。

    然后在客户端调用定义的aidl里面的方法,与服务端进行通信。

    // addBookIn
    Logger.i("-------- addBookIn() ------------------------");
    int nextIntIn = RandomUtils.randomInt(10000);
    BookBean bookBeanIn = new BookBean("书名-" + nextIntIn, "作者-" + nextIntIn,
                                (RandomUtils.randomInt(10000) + 10000) / 100d);
    Logger.i(RemoteBinderService.SERVICE_NAME + " addBookIn() before info:" + bookBeanIn.toString());
    iRemoteBookBinder.addBookIn(bookBeanIn);
    Logger.i(RemoteBinderService.SERVICE_NAME + " addBookIn() after info:" + bookBeanIn.toString());
    
    // addBookOut
    Logger.i("-------- addBookOut() ------------------------");
    int nextIntOut = RandomUtils.randomInt(10000);
    BookBean bookBeanOut = new BookBean("书名-" + nextIntOut, "作者-" + nextIntOut,
                                (RandomUtils.randomInt(10000) + 10000) / 100d);
    Logger.i(RemoteBinderService.SERVICE_NAME + " addBookOut() before info:" + bookBeanOut.toString());
    iRemoteBookBinder.addBookOut(bookBeanOut);
    Logger.i(RemoteBinderService.SERVICE_NAME + " addBookOut() after info:" + bookBeanOut.toString());
    
    // addBookInOut
    Logger.i("-------- addBookInOut() ------------------------");
    int nextIntInOut = RandomUtils.randomInt(10000);
    BookBean bookBeanInOut = new BookBean("书名-" + nextIntInOut, "作者-" + nextIntInOut,
                                (RandomUtils.randomInt(10000) + 10000) / 100d);
    Logger.i(RemoteBinderService.SERVICE_NAME + " addBookInOut() before info:" + bookBeanInOut.toString());
    iRemoteBookBinder.addBookInOut(bookBeanInOut);
    Logger.i(RemoteBinderService.SERVICE_NAME + " addBookInOut() after info:" + bookBeanInOut.toString());
    
    Logger.i("-------------------------------------------------");
    

    然后在服务端进行数据修改,得到的日志为:

    2021-01-19 10:46:08.613 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(90): -------- addBookIn() ------------------------
    2021-01-19 10:46:08.614 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(94): zzf addBookIn() before info:BookBean{bookName='书名-8832', bookAuthor='作者-8832', bookPrice=140.58}
    2021-01-19 10:46:08.617 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookIn(48): ------------------------服务端修改前的数据----------------------
    2021-01-19 10:46:08.619 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookIn(49): BookBean{bookName='书名-8832', bookAuthor='作者-8832', bookPrice=140.58} ,Progress Name: com.renj.remote.binder ,Thread Name: Binder:29178_1
    2021-01-19 10:46:08.619 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookIn(50): --------------------------------------------------------------
    2021-01-19 10:46:08.619 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookIn(53): ------------------------服务端修改后的数据----------------------
    2021-01-19 10:46:08.621 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookIn(54): BookBean{bookName='书名-8832', bookAuthor='zzf', bookPrice=140.58} ,Progress Name: com.renj.remote.binder ,Thread Name: Binder:29178_1
    2021-01-19 10:46:08.621 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookIn(55): --------------------------------------------------------------
    2021-01-19 10:46:08.622 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(96): zzf addBookIn() after info:BookBean{bookName='书名-8832', bookAuthor='作者-8832', bookPrice=140.58}
    2021-01-19 10:46:08.622 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(99): -------- addBookOut() ------------------------
    2021-01-19 10:46:08.623 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(103): zzf addBookOut() before info:BookBean{bookName='书名-9785', bookAuthor='作者-9785', bookPrice=145.96}
    2021-01-19 10:46:08.623 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookOut(60): ------------------------服务端修改前的数据----------------------
    2021-01-19 10:46:08.624 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookOut(61): BookBean{bookName='null', bookAuthor='null', bookPrice=0.0} ,Progress Name: com.renj.remote.binder ,Thread Name: Binder:29178_1
    2021-01-19 10:46:08.625 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookOut(62): --------------------------------------------------------------
    2021-01-19 10:46:08.625 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookOut(65): ------------------------服务端修改后的数据----------------------
    2021-01-19 10:46:08.626 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookOut(66): BookBean{bookName='null', bookAuthor='zzf', bookPrice=0.0} ,Progress Name: com.renj.remote.binder ,Thread Name: Binder:29178_1
    2021-01-19 10:46:08.626 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookOut(67): --------------------------------------------------------------
    2021-01-19 10:46:08.626 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(105): zzf addBookOut() after info:BookBean{bookName='null', bookAuthor='zzf', bookPrice=0.0}
    2021-01-19 10:46:08.626 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(108): -------- addBookInOut() ------------------------
    2021-01-19 10:46:08.627 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(112): zzf addBookInOut() before info:BookBean{bookName='书名-8313', bookAuthor='作者-8313', bookPrice=105.42}
    2021-01-19 10:46:08.627 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookInOut(72): ------------------------服务端修改前的数据----------------------
    2021-01-19 10:46:08.628 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookInOut(73): BookBean{bookName='书名-8313', bookAuthor='作者-8313', bookPrice=105.42} ,Progress Name: com.renj.remote.binder ,Thread Name: Binder:29178_1
    2021-01-19 10:46:08.628 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookInOut(74): --------------------------------------------------------------
    2021-01-19 10:46:08.628 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookInOut(77): ------------------------服务端修改后的数据----------------------
    2021-01-19 10:46:08.629 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookInOut(78): BookBean{bookName='书名-8313', bookAuthor='zzf', bookPrice=105.42} ,Progress Name: com.renj.remote.binder ,Thread Name: Binder:29178_1
    2021-01-19 10:46:08.629 29178-29196/com.renj.remote.binder I/Logger: RemoteBinderService$RemoteBookBinderImpl.addBookInOut(79): --------------------------------------------------------------
    2021-01-19 10:46:08.630 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(114): zzf addBookInOut() after info:BookBean{bookName='书名-8313', bookAuthor='zzf', bookPrice=105.42}
    2021-01-19 10:46:08.630 29100-29100/com.example.service I/Logger: MainActivity$5.onClick(116): -------------------------------------------------
    

    从日志里可以看出,当参数用in 修饰时,我们修改服务端的数据,并不会影响客户端的值,而用out修饰时,服务端修改后,客户端也会修改,inout修饰时,服务端修改后,客户端也会修改。下面我们来看为什么会这样。

    in

    当我们客户端调用下面代码时:

    iRemoteBookBinder.addBookIn(bookBeanIn);
    
     @Override public void addBookIn(com.example.service.bean.BookBean bookBean) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((bookBean!=null)) {
                _data.writeInt(1);//1
                bookBean.writeToParcel(_data, 0);//2
              }
              else {
                _data.writeInt(0);
              }
              boolean _status = mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().addBookIn(bookBean);
                return;
              }
              _reply.readException();
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
    

    注释1的作用是用来在服务端判断用的。

    注释2会调用

    @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(bookName);
            dest.writeString(bookAuthor);
            dest.writeDouble(bookPrice);
        }
    

    将bookBean写入到_data中,然后调用transact();

    通过底层一系列的调用,最后会调用stub的onTranact(),具体分析在下面链接:

    https://www.imooc.com/article/49742

     case TRANSACTION_addBookIn:
            {
              data.enforceInterface(descriptor);
              com.example.service.bean.BookBean _arg0;
              if ((0!=data.readInt())) {
                _arg0 = com.example.service.bean.BookBean.CREATOR.createFromParcel(data);
              }
              else {
                _arg0 = null;
              }
              //然后在服务端调用Porxy的addBookIn(),这样最终会调用客户端的onTranact,
              //将结果返回回去,在前面我就说过,Stub和Proxy在客户端和服务端同时都存在,而不是Proxy存在客户端,stub存在服务端。一般是从Proxy开始请求,Stub去接收处理请求。
              this.addBookIn(_arg0);
              reply.writeNoException();
              return true;
            }
    

    先创建一个BookBean对象,调用createFromParcel():

    public static final Creator<BookBean> CREATOR = new Creator<BookBean>() {
            @Override
            public BookBean createFromParcel(Parcel in) {
                return new BookBean(in);
            }
    
            @Override
            public BookBean[] newArray(int size) {
                return new BookBean[size];
            }
        };
    

    通过new的方式创建一个实例,然后将传过来的值赋值进去。

    分析了整个流程,我们知道,调用期间传入的对象book只是将数据写入到Server,它的值进行并没有任何修改,就直接返回。所以in类型的参数,它向服务端传入数据,但是却不接受Server返回的值。

    out
    @Override public void addBookOut(com.example.service.bean.BookBean bookBean) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              boolean _status = mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().addBookOut(bookBean);
                return;
              }
              _reply.readException();
              
              if ((0!=_reply.readInt())) {
                bookBean.readFromParcel(_reply);//
              }
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
            
    case TRANSACTION_addBookOut:
            {
              data.enforceInterface(descriptor);
              com.example.service.bean.BookBean _arg0;
              _arg0 = new com.example.service.bean.BookBean();//这里直接new一个空对象,没有把传过来的值赋值进去
              this.addBookOut(_arg0);//调用这个方法将修改的字段写入_arg0
              reply.writeNoException();
              if ((_arg0!=null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//将修改后的_arg0写入reply
              }
              else {
                reply.writeInt(0);
              }
              return true;
            }
    

    整个方法调用期间传入的对象book并没有将数据写入到Server,它的值确实是Server返回的。

    总结:out类型的参数,它并不向服务端传入数据,但是却接受Server返回的值。

    inout
    @Override public void addBookInOut(com.example.service.bean.BookBean bookBean) throws android.os.RemoteException
          {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((bookBean!=null)) {
                _data.writeInt(1);
                bookBean.writeToParcel(_data, 0);
              }
              else {
                _data.writeInt(0);
              }
              boolean _status = mRemote.transact(Stub.TRANSACTION_addBookInOut, _data, _reply, 0);
              if (!_status && getDefaultImpl() != null) {
                getDefaultImpl().addBookInOut(bookBean);
                return;
              }
              _reply.readException();
              if ((0!=_reply.readInt())) {
                bookBean.readFromParcel(_reply);
              }
            }
            finally {
              _reply.recycle();
              _data.recycle();
            }
          }
          
    case TRANSACTION_addBookInOut:
            {
              data.enforceInterface(descriptor);
              com.example.service.bean.BookBean _arg0;
              if ((0!=data.readInt())) {
                _arg0 = com.example.service.bean.BookBean.CREATOR.createFromParcel(data);//将接收到的数据赋值到_arg0
              }
              else {
                _arg0 = null;
              }
              this.addBookInOut(_arg0);
              reply.writeNoException();
              if ((_arg0!=null)) {
                reply.writeInt(1);
                _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//将修改后的_arg0写入reply
              }
              else {
                reply.writeInt(0);
              }
              return true;
            }
    

    从上面可以看出,在inout中,是将接收到的data进行修改并返回。

    总结:inout类型的参数,它既向服务端传入数据,也却接受Server返回的值。

    最后用一张图去充分理解。

    1611049703(1).png

    相关文章

      网友评论

          本文标题:AIDL之定向Tag

          本文链接:https://www.haomeiwen.com/subject/pzlczktx.html