• 细说Binder(Binder核心原理最全解析)


    前面写了一篇关于AIDL的文章,那我们就从AIDL谈起吧。如果对AIDL有不理解的,可以先看看这一篇文章:

    再探AIDL_AD钙奶-lalala的博客-CSDN博客

    我们还是延续前面一篇文章的例子来讲解,我们创建的AIDL文件如下:

    1. // IMyAidlInterface.aidl
    2. package com.example.aidl;
    3. // Declare any non-default types here with import statements
    4. interface IMyAidlInterface {
    5. String getUserName();
    6. String getPassword();
    7. }

    然后Build->Make Project,会自动在build->generated->aidl_source_output_dir里面生成固定格式的文件。想了解Binder的原理,就先来了解一下AIDL吧。

    生成的文件如下:

    1. /*
    2. * This file is auto-generated. DO NOT MODIFY.
    3. */
    4. package com.example.aidl;
    5. // Declare any non-default types here with import statements
    6. public interface IMyAidlInterface extends android.os.IInterface
    7. {
    8. /** Default implementation for IMyAidlInterface. */
    9. public static class Default implements com.example.aidl.IMyAidlInterface
    10. {
    11. @Override public java.lang.String getUserName() throws android.os.RemoteException
    12. {
    13. return null;
    14. }
    15. @Override public java.lang.String getPassword() throws android.os.RemoteException
    16. {
    17. return null;
    18. }
    19. @Override
    20. public android.os.IBinder asBinder() {
    21. return null;
    22. }
    23. }
    24. /** Local-side IPC implementation stub class. */
    25. public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyAidlInterface
    26. {
    27. private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyAidlInterface";
    28. /** Construct the stub at attach it to the interface. */
    29. public Stub()
    30. {
    31. this.attachInterface(this, DESCRIPTOR);
    32. }
    33. /**
    34. * Cast an IBinder object into an com.example.aidl.IMyAidlInterface interface,
    35. * generating a proxy if needed.
    36. */
    37. public static com.example.aidl.IMyAidlInterface asInterface(android.os.IBinder obj)
    38. {
    39. if ((obj==null)) {
    40. return null;
    41. }
    42. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    43. if (((iin!=null)&&(iin instanceof com.example.aidl.IMyAidlInterface))) {
    44. return ((com.example.aidl.IMyAidlInterface)iin);
    45. }
    46. return new com.example.aidl.IMyAidlInterface.Stub.Proxy(obj);
    47. }
    48. @Override public android.os.IBinder asBinder()
    49. {
    50. return this;
    51. }
    52. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    53. {
    54. java.lang.String descriptor = DESCRIPTOR;
    55. switch (code)
    56. {
    57. case INTERFACE_TRANSACTION:
    58. {
    59. reply.writeString(descriptor);
    60. return true;
    61. }
    62. case TRANSACTION_getUserName:
    63. {
    64. data.enforceInterface(descriptor);
    65. java.lang.String _result = this.getUserName();
    66. reply.writeNoException();
    67. reply.writeString(_result);
    68. return true;
    69. }
    70. case TRANSACTION_getPassword:
    71. {
    72. data.enforceInterface(descriptor);
    73. java.lang.String _result = this.getPassword();
    74. reply.writeNoException();
    75. reply.writeString(_result);
    76. return true;
    77. }
    78. default:
    79. {
    80. return super.onTransact(code, data, reply, flags);
    81. }
    82. }
    83. }
    84. private static class Proxy implements com.example.aidl.IMyAidlInterface
    85. {
    86. private android.os.IBinder mRemote;
    87. Proxy(android.os.IBinder remote)
    88. {
    89. mRemote = remote;
    90. }
    91. @Override public android.os.IBinder asBinder()
    92. {
    93. return mRemote;
    94. }
    95. public java.lang.String getInterfaceDescriptor()
    96. {
    97. return DESCRIPTOR;
    98. }
    99. @Override public java.lang.String getUserName() throws android.os.RemoteException
    100. {
    101. android.os.Parcel _data = android.os.Parcel.obtain();
    102. android.os.Parcel _reply = android.os.Parcel.obtain();
    103. java.lang.String _result;
    104. try {
    105. _data.writeInterfaceToken(DESCRIPTOR);
    106. boolean _status = mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
    107. if (!_status && getDefaultImpl() != null) {
    108. return getDefaultImpl().getUserName();
    109. }
    110. _reply.readException();
    111. _result = _reply.readString();
    112. }
    113. finally {
    114. _reply.recycle();
    115. _data.recycle();
    116. }
    117. return _result;
    118. }
    119. @Override public java.lang.String getPassword() throws android.os.RemoteException
    120. {
    121. android.os.Parcel _data = android.os.Parcel.obtain();
    122. android.os.Parcel _reply = android.os.Parcel.obtain();
    123. java.lang.String _result;
    124. try {
    125. _data.writeInterfaceToken(DESCRIPTOR);
    126. boolean _status = mRemote.transact(Stub.TRANSACTION_getPassword, _data, _reply, 0);
    127. if (!_status && getDefaultImpl() != null) {
    128. return getDefaultImpl().getPassword();
    129. }
    130. _reply.readException();
    131. _result = _reply.readString();
    132. }
    133. finally {
    134. _reply.recycle();
    135. _data.recycle();
    136. }
    137. return _result;
    138. }
    139. public static com.example.aidl.IMyAidlInterface sDefaultImpl;
    140. }
    141. static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    142. static final int TRANSACTION_getPassword = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    143. public static boolean setDefaultImpl(com.example.aidl.IMyAidlInterface impl) {
    144. // Only one user of this interface can use this function
    145. // at a time. This is a heuristic to detect if two different
    146. // users in the same process use this function.
    147. if (Stub.Proxy.sDefaultImpl != null) {
    148. throw new IllegalStateException("setDefaultImpl() called twice");
    149. }
    150. if (impl != null) {
    151. Stub.Proxy.sDefaultImpl = impl;
    152. return true;
    153. }
    154. return false;
    155. }
    156. public static com.example.aidl.IMyAidlInterface getDefaultImpl() {
    157. return Stub.Proxy.sDefaultImpl;
    158. }
    159. }
    160. public java.lang.String getUserName() throws android.os.RemoteException;
    161. public java.lang.String getPassword() throws android.os.RemoteException;
    162. }

    这段生成的代码比较长,我们来拆解分析:

    首先看asInterface方法,会走到new com.example.aidl.IMyAidlInterface.Stub.Proxy(obj),其他的不用管。我们接着看,赋值给了android.os.IBinder mRemote,怎么理解这个mRemote呢?我们知道Remote是远程的意思,所以可以理解为服务端。asInterface最终会返回一个Proxy对象。我们客户端后面会调用getUserName等方法,其实就是在调用Proxy的getUserName等方法。我们在看getUserName方法到底做了什么:

    1. @Override public java.lang.String getUserName()
    2. throws android.os.RemoteException
    3. {
    4. android.os.Parcel _data = android.os.Parcel.obtain();
    5. android.os.Parcel _reply = android.os.Parcel.obtain();
    6. java.lang.String _result;
    7. try {
    8. _data.writeInterfaceToken(DESCRIPTOR);
    9. boolean _status = mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
    10. if (!_status && getDefaultImpl() != null) {
    11. return getDefaultImpl().getUserName();
    12. }
    13. _reply.readException();
    14. _result = _reply.readString();
    15. }
    16. finally {
    17. _reply.recycle();
    18. _data.recycle();
    19. }
    20. return _result;
    21. }

    Parcel简单理解为序列化读写数据的类,核心调用逻辑来了:

    mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);

    这个时候就开始了真正的进程间通信(IPC)了,可能大家不太理解。我们回到AIDL客户端调用:

    1. private final ServiceConnection serviceConnection = new ServiceConnection() {
    2. @Override
    3. public void onServiceConnected(ComponentName name, IBinder service) {
    4. IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
    5. try {
    6. userName = iMyAidlInterface.getUserName();
    7. password = iMyAidlInterface.getPassword();
    8. } catch (RemoteException e) {
    9. e.printStackTrace();
    10. }
    11. }
    12. @Override
    13. public void onServiceDisconnected(ComponentName name) {
    14. }
    15. };

    asInterface流程前面已经经清楚了,最终我们会调用transact方法,mRemote我们前面说可以理解为服务端,其实更加准确的说,应该理解为服务端在客户端进程的代理。transact方法调用后在服务端进程里面就会调用onTransact方法,下面截取一段分析:

    1. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    2. {
    3. ···
    4. switch (code)
    5. {
    6. ···
    7. case TRANSACTION_getUserName:
    8. {
    9. data.enforceInterface(descriptor);
    10. java.lang.String _result = this.getUserName();
    11. reply.writeNoException();
    12. reply.writeString(_result);
    13. return true;
    14. }
    15. ···
    16. }
    17. }

    首先要明白这段代码是运行在服务端进程里面的,在客户端transact方法调用之后开始回调。code就是transact的第一个参数,用来区分调用的方法名称。然后把数据写进Parcel类型的reply里面。最终再从客户端的reply里面读出来。说到这里,想必大家已经对AIDL的大概流程有了一个较为深刻的理解。

    通过AIDL这样一个例子,我们不禁要问:Binder是如何实现跨进程通信的呢?

    我们先来看传统的进程间通信方式:

     既然Linux已经有了进程间通信的方式,那么Android为什么还要设计一套Binder机制呢?

    换一个问法:Binder机制为什么可以一次拷贝?

    这个时候我们就必须搞清楚一些前置概念:Linux虚拟内存和物理内存_AD钙奶-lalala的博客-CSDN博客_linux虚拟内存和物理内存

    未完待续!

  • 相关阅读:
    江苏全面推进双重预防数字化建设,强化落实危化企业主体责任
    弘辽科技:拼多多商家如何自己提升销量?提升销量需要注意什么?
    java计算机毕业设计企业公开招聘系统源程序+mysql+系统+lw文档+远程调试
    瑞芯微rk1126 编译mp4v2记录 rk1126移植 ffmpeg
    力扣 572. 另一棵树的子树
    Nginx配置反向代理解决跨域问题
    Windows 安装 MariaDB 数据库
    ts 类型体操 Chainable Options 可链式选项
    ArduPilot开源飞控之AP_Relay
    抛弃真实数据集?生成式AI“踢馆”人工智能下半场
  • 原文地址:https://blog.csdn.net/qq_36428821/article/details/125628443