AIDL oneway 以及in、out、inout参数的理解

比眉伴天荒 2021-11-09 04:50 250阅读 0赞

oneway

oneway可以用来修饰在interface之前,这样会造成interface内所有的方法都隐式地带上oneway;
oneway也可以修饰在interface里的各个方法之前。
被oneway修饰了的方法不可以有返回值,也不可以有带out或inout的参数。

带oneway的实现

带oneway的方法,不会生成局部变量_reply。且Proxy中transact中第四个参数必为android.os.IBinder.FLAG_ONEWAY

  1. //proxy类
  2. @Override public void testOneway(int pa) throws android.os.RemoteException
  3. {
  4. android.os.Parcel _data = android.os.Parcel.obtain();
  5. try {
  6. _data.writeInterfaceToken(DESCRIPTOR);
  7. _data.writeInt(pa);
  8. mRemote.transact(Stub.TRANSACTION_testOneway, _data, null, android.os.IBinder.FLAG_ONEWAY);
  9. }
  10. finally {
  11. _data.recycle();
  12. }
  13. }
  14. //stub类
  15. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  16. {
  17. case TRANSACTION_testOneway:
  18. {
  19. data.enforceInterface(descriptor);
  20. int _arg0;
  21. _arg0 = data.readInt();
  22. this.testOneway(_arg0);
  23. return true;
  24. }
  25. }

不带oneway的实现

不带oneway的方法,会生成局部变量_reply,但当方法返回值为void时,不会生成局部变量_result,这个才是真正的返回值。且Proxy中transact中第四个参数必为0

  1. //proxy类
  2. @Override public byte SerTestIn(byte[] pa) throws android.os.RemoteException
  3. {
  4. android.os.Parcel _data = android.os.Parcel.obtain();
  5. android.os.Parcel _reply = android.os.Parcel.obtain();
  6. byte _result;
  7. try {
  8. _data.writeInterfaceToken(DESCRIPTOR);
  9. _data.writeByteArray(pa);
  10. mRemote.transact(Stub.TRANSACTION_SerTestIn, _data, _reply, 0);
  11. _reply.readException();
  12. _result = _reply.readByte();
  13. }
  14. finally {
  15. _reply.recycle();
  16. _data.recycle();
  17. }
  18. return _result;
  19. }
  20. //stub类
  21. case TRANSACTION_SerTestIn:
  22. {
  23. data.enforceInterface(descriptor);
  24. byte[] _arg0;
  25. _arg0 = data.createByteArray();
  26. byte _result = this.SerTestIn(_arg0);
  27. reply.writeNoException();
  28. reply.writeByte(_result);
  29. return true;
  30. }

in、out、inout参数

为了测试,定义了如下AIDL文件:

  1. package com.java.prac;
  2. import com.java.prac.IListener;
  3. interface IService {
  4. void registerListener(in IListener listener);
  5. void unregisterListener(in IListener listener);
  6. byte SerTestIn(in byte[] pa);
  7. byte SerTestOut(out byte[] pa);
  8. byte SerTestInout(inout byte[] pa);
  9. }

为了方便起见,把proxy类称为调用方,stub类称为实现方。在跨进程调用中,参数和返回值肯定都是会有复制的过程的,由于系统的设计,将内核空间再映射到用户空间,这样复制过程只需要一次。下面代码都是从自动生成的java代码抽取而来。

in参数

  1. //proxy类
  2. @Override public byte SerTestIn(byte[] pa) throws android.os.RemoteException
  3. {
  4. android.os.Parcel _data = android.os.Parcel.obtain();
  5. android.os.Parcel _reply = android.os.Parcel.obtain();
  6. byte _result;
  7. try {
  8. _data.writeInterfaceToken(DESCRIPTOR);
  9. _data.writeByteArray(pa);
  10. mRemote.transact(Stub.TRANSACTION_SerTestIn, _data, _reply, 0);
  11. _reply.readException();
  12. _result = _reply.readByte();
  13. }
  14. finally {
  15. _reply.recycle();
  16. _data.recycle();
  17. }
  18. return _result;
  19. }
  20. //stub类
  21. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  22. {
  23. ...
  24. case TRANSACTION_SerTestIn:
  25. {
  26. data.enforceInterface(descriptor);
  27. byte[] _arg0;
  28. _arg0 = data.createByteArray();
  29. byte _result = this.SerTestIn(_arg0);
  30. reply.writeNoException();
  31. reply.writeByte(_result);
  32. return true;
  33. }
  34. ...
  35. }
  • proxy调用了_data.writeByteArray(pa);,说明pa这个参数确实传递到了服务方。但stub里只有reply.writeByte(_result);,说明服务方对pa参数的任何改变都不会反应到调用方。

out参数

  1. //proxy类
  2. @Override public byte SerTestOut(byte[] pa) throws android.os.RemoteException
  3. {
  4. android.os.Parcel _data = android.os.Parcel.obtain();
  5. android.os.Parcel _reply = android.os.Parcel.obtain();
  6. byte _result;
  7. try {
  8. _data.writeInterfaceToken(DESCRIPTOR);
  9. if ((pa==null)) {
  10. _data.writeInt(-1);
  11. }
  12. else {
  13. _data.writeInt(pa.length);
  14. }
  15. mRemote.transact(Stub.TRANSACTION_SerTestOut, _data, _reply, 0);
  16. _reply.readException();
  17. _result = _reply.readByte();
  18. _reply.readByteArray(pa);
  19. }
  20. finally {
  21. _reply.recycle();
  22. _data.recycle();
  23. }
  24. return _result;
  25. }
  26. //stub类
  27. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  28. {
  29. ...
  30. case TRANSACTION_SerTestOut:
  31. {
  32. data.enforceInterface(descriptor);
  33. byte[] _arg0;
  34. int _arg0_length = data.readInt();
  35. if ((_arg0_length<0)) {
  36. _arg0 = null;
  37. }
  38. else {
  39. _arg0 = new byte[_arg0_length];
  40. }
  41. byte _result = this.SerTestOut(_arg0);
  42. reply.writeNoException();
  43. reply.writeByte(_result);
  44. reply.writeByteArray(_arg0);
  45. return true;
  46. }
  47. ...
  48. }
  • proxy类里面,发现读取参数的时候,仅仅是_data.writeInt(pa.length);读取一下数组的长度,而不是读取数组的内容。
  • stub类里面,发现它居然用之前读取的数组长度新建了一个数组_arg0 = new byte[_arg0_length];,然后把这个新建的同样长度的数组传递给了真正的方法,难道我们pa参数这个数组的内容都不重要吗,仅仅是为了告诉对方数组的长度吗?(你眉头一皱发现事情并不简单==)事实上,就是这么简单,pa数组的长度才是有用信息。
  • stub类里面,开始设置返回值时,发现多了一步reply.writeByteArray(_arg0);,其实是服务方对新建的数组赋值了,然后要作为返回值返回。
  • proxy类里面,既然服务方要返回调用方一个数组,那就接受吧,_reply.readByteArray(pa);,然后把pa进行赋值。

inout参数

  1. //proxy类
  2. @Override public byte SerTestInout(byte[] pa) throws android.os.RemoteException
  3. {
  4. android.os.Parcel _data = android.os.Parcel.obtain();
  5. android.os.Parcel _reply = android.os.Parcel.obtain();
  6. byte _result;
  7. try {
  8. _data.writeInterfaceToken(DESCRIPTOR);
  9. _data.writeByteArray(pa);
  10. mRemote.transact(Stub.TRANSACTION_SerTestInout, _data, _reply, 0);
  11. _reply.readException();
  12. _result = _reply.readByte();
  13. _reply.readByteArray(pa);
  14. }
  15. finally {
  16. _reply.recycle();
  17. _data.recycle();
  18. }
  19. return _result;
  20. }
  21. //stub类
  22. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  23. {
  24. ...
  25. case TRANSACTION_SerTestInout:
  26. {
  27. data.enforceInterface(descriptor);
  28. byte[] _arg0;
  29. _arg0 = data.createByteArray();
  30. byte _result = this.SerTestInout(_arg0);
  31. reply.writeNoException();
  32. reply.writeByte(_result);
  33. reply.writeByteArray(_arg0);
  34. return true;
  35. }
  36. ...
  37. }
  • 分析和上面类似了。
  • 服务方会完好无损地收到调用方发来的pa数组。
  • 服务方可能会对pa数组进行修改,然后再返回给调用方。

总结

  • in参数使得实参顺利传到服务方,但服务方对实参的任何改变,不会反应回调用方。
  • out参数使得实参不会真正传到服务方,只是传一个实参的初始值过去(这里实参只是作为返回值来使用的,这样除了return那里的返回值,还可以返回另外的东西),但服务方对实参的任何改变,在调用结束后会反应回调用方。
  • inout参数则是上面二者的结合,实参会顺利传到服务方,且服务方对实参的任何改变,在调用结束后会反应回调用方。
  • 其实inout,都是相对于服务方。in参数使得实参传到了服务方,所以是in进入了服务方;out参数使得实参在调用结束后从服务方传回给调用方,所以是out从服务方出来。

发表评论

表情:
评论列表 (有 0 条评论,250人围观)

还没有评论,来说两句吧...

相关阅读