[关闭]
@TryLoveCatch 2023-05-04T17:32:35.000000Z 字数 14462 阅读 1595

Android知识体系之AIDL

Android知识体系


一篇文章了解相见恨晚的 Android Binder 进程间通讯机制

AIDL使用

Server端

  1. interface IMyAidlInterface {
  2. String getName();
  3. }

这个就是我们这个Server提供的功能。

app->build->generated->source->aidl->debug路径下面生成了IMyAidlInterface.java

  1. public class MyService extends Service{
  2. @Override
  3. public void onCreate() {
  4. super.onCreate();
  5. }
  6. @Nullable
  7. @Override
  8. public IBinder onBind(Intent intent) {
  9. // 检查权限
  10. int tCheck = checkCallingOrSelfPermission("io.github.trylovecatch.AIDL_SERVICE");
  11. if(tCheck == PackageManager.PERMISSION_DENIED){
  12. return null;
  13. }
  14. return new MyBinder();
  15. }
  16. class MyBinder extends IMyAidlInterface.Stub
  17. {
  18. @Override
  19. public String getName() throws RemoteException
  20. {
  21. return "哈哈哈";
  22. }
  23. }
  24. }
  1. <permission android:protectionLevel="normal" android:name="io.github.trylovecatch.AIDL_SERVICE"/>

注册MyService

  1. <service
  2. android:name=".MyService"
  3. android:enabled="true"
  4. android:exported="true">
  5. <intent-filter>
  6. <action android:name="io.github.trylovecatch.AIDL_SERVICE" />
  7. </intent-filter>
  8. </service>

好了,Server端到此,就全部结束了。

Client端

  1. Intent tIntent = new Intent("io.github.trylovecatch.AIDL_SERVICE");
  2. tIntent.setPackage("io.github.trylovecatch.aidlserver");
  3. mServiceConnection = new ServiceConnection() {
  4. @Override
  5. public void onServiceConnected(ComponentName name, IBinder service) {
  6. mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
  7. }
  8. @Override
  9. public void onServiceDisconnected(ComponentName name) {
  10. }
  11. };
  12. bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
  13. findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
  14. @Override
  15. public void onClick(View v) {
  16. try {
  17. Toast.makeText(MainActivity.this, mAidlInterface.getName(), Toast.LENGTH_LONG).show();
  18. } catch (RemoteException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. });
  23. @Override
  24. protected void onDestroy() {
  25. super.onDestroy();
  26. unbindService(mServiceConnection);
  27. }
  1. <uses-permission android:name="io.github.trylovecatch.AIDL_SERVICE"/>

总结

整个过程就是这样,当点击Button的时候,就会Toast显示哈哈哈,证明远程调用成功。
代码下载: 源代码


Binder机制

背景知识

进程隔离

进程隔离是为保护操作系统中进程互不干扰而设计的一组不同硬件和软件的技术。这个技术是为了避免进程A写入进程B的情况发生。 进程的隔离实现,使用了虚拟地址空间。进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A将数据信息写入进程B

操作系统的不同进程之间,数据不共享;对于每个进程来说,它都天真地以为自己独享了整个系统,完全不知道其他进程的存在;因此一个进程需要与另外一个进程通信,需要某种系统机制才能完成。

用户空间/内核空间

首先,我们说下Linux Kernel

它是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。

内核空间

Linux Kernel的运行空间,就是Kernel space(内核空间)
Kernel space 可以执行任意命令,调用系统的一切资源。

用户空间

用户程序的运行空间,就是User space(用户空间)
User space 只能执行简单的运算,不能直接调用系统资源,必须通过系统调用(system call),才能向内核发出指令。

为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。这样就对Linux Kernel起到了保护作用。

举个例子:

  1. str = "my string" // 用户空间
  2. x = x + 2
  3. file.write(str) // 切换到内核空间
  4. y = x + 4 // 切换回用户空间

第三行需要写入文件,就要切换到 Kernel space,因为用户不能直接写文件,必须通过内核安排

系统调用/内核态/用户态

虽然从逻辑上抽离出用户空间和内核空间;但是不可避免的的是,总有那么一些用户空间需要访问内核的资源;比如应用程序访问文件,网络是很常见的事情,怎么办呢?
上面我们说过了

用户空间访问内核空间的唯一方式就是系统调用(system call)

通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。用户软件良莠不齐,要是它们乱搞把系统玩坏了怎么办?因此对于某些特权操作必须交给安全可靠的内核来执行。

内核运行态(内核态),指一个任务(进程)执行系统调用而陷入内核代码中时。例如上面例子的第三行。

用户运行态(用户态),当进程在执行用户自己的代码时。例如上面例子的第一、二、四行。

内核模块/驱动

通过系统调用用户空间可以访问内核空间,那么如果一个用户空间想与另外一个用户空间进行通信怎么办呢?

我们希望,最好操作系统内核能够支持,事实上,确实是支持的:传统的Linux通信机制,比如Socket管道等都是内核支持的。

但是,Binder并不是Linux 内核的一部分,它是怎么做到访问内核空间的呢?

答案就是:动态可加载内核模块(Loadable Kernel Module,LKM)机制。

LKM是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。

这样,Android系统可以通过添加一个内核模块运行在内核空间用户空间之间可以通过这个模块作为桥梁,来完成通信了。

Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动;它是一种虚拟的物理设备,位于/dev/binder路径下。

为什么使用Binder?

  1. 采用C/S通信模式。
  2. 性能。
    • socket:是一个通用接口,导致其传输效率低,开销大;
    • 管道和消息队列:因为采用存储转发方式,所以至少需要拷贝2次数据,效率低;
    • 共享内存:虽然在传输时没有拷贝数据,但其控制机制复杂
  3. 安全。Linux进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造;而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性.

Binder通信模型

通过上面的介绍,我们知道:

用户空间A要和用户空间B通信,必须借助内核空间的支持,而Android通过LKM,添加了一个内核模块内核空间,这个内核模块,就是Binder驱动

我们举一个现实中的例子,来说明这个过程。
假设AB要进行通信,通信的媒介是打电话(AClientBServer)。A要给B通信:

这里面有四个角色:
1. BServer,提供服务,并把自己写入通讯录里面
2. AClient,发起通信的,需要B的服务
3. 通讯录,保存了Server的对应关系,叫做ServiceManager(SM)
4. 基站,类似于Binder驱动,通过它的帮助,才能彼此联系上

整个通信步骤如下:

这里ClientSM的通信,以及ClientServer的通信,都会经过Binder驱动Binder驱动是整个通信过程的核心。

Binder机制跨进程原理

上文给出了Binder的通信模型,指出了通信过程的四个角色: Client, Server, SM, driver; 但是我们仍然不清楚Client到底是如何与Server完成通信的。

两个运行在用户空间的进程A和进程B如何完成通信呢?内核可以访问A和B的所有数据;所以,最简单的方式是通过内核做中转;假设进程A要给进程B发送数据,那么就先把A的数据copy到内核空间,然后把内核空间对应的数据copy到B就完成了;用户空间要操作内核空间,需要通过系统调用;刚好,这里就有两个系统调用:copy_from_user, copy_to_user

但是,Binder机制并不是这么干的。讲这么一段,是说明进程间通信并不是什么神秘的东西。那么,Binder机制是如何实现跨进程通信的呢?

简单理解

首先,看一下系统根据我们的aidl文件生成的java文件,路径在app->build->generated->source->aidl->debug

  1. package io.github.trylovecatch.aidlserver;
  2. // Declare any non-default types here with import statements
  3. public interface IMyAidlInterface extends android.os.IInterface {
  4. /**
  5. * Local-side IPC implementation stub class.
  6. */
  7. public static abstract class Stub extends android.os.Binder
  8. implements io.github.trylovecatch.aidlserver.IMyAidlInterface {
  9. private static final java.lang.String DESCRIPTOR = "io.github.trylovecatch.aidlserver.IMyAidlInterface";
  10. /**
  11. * Construct the stub at attach it to the interface.
  12. */
  13. public Stub() {
  14. this.attachInterface(this, DESCRIPTOR);
  15. }
  16. /**
  17. * Cast an IBinder object into an io.github.trylovecatch.aidlserver.IMyAidlInterface interface,
  18. * generating a proxy if needed.
  19. */
  20. public static io.github.trylovecatch.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {
  21. if ((obj == null)) {
  22. return null;
  23. }
  24. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  25. if (((iin != null) && (iin instanceof io.github.trylovecatch.aidlserver.IMyAidlInterface))) {
  26. return ((io.github.trylovecatch.aidlserver.IMyAidlInterface) iin);
  27. }
  28. return new io.github.trylovecatch.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
  29. }
  30. @Override
  31. public android.os.IBinder asBinder() {
  32. return this;
  33. }
  34. @Override
  35. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
  36. throws android.os.RemoteException {
  37. switch (code) {
  38. case INTERFACE_TRANSACTION: {
  39. reply.writeString(DESCRIPTOR);
  40. return true;
  41. }
  42. case TRANSACTION_getName: {
  43. data.enforceInterface(DESCRIPTOR);
  44. java.lang.String _result = this.getName();
  45. reply.writeNoException();
  46. reply.writeString(_result);
  47. return true;
  48. }
  49. }
  50. return super.onTransact(code, data, reply, flags);
  51. }
  52. private static class Proxy implements io.github.trylovecatch.aidlserver.IMyAidlInterface {
  53. private android.os.IBinder mRemote;
  54. Proxy(android.os.IBinder remote) {
  55. mRemote = remote;
  56. }
  57. @Override
  58. public android.os.IBinder asBinder() {
  59. return mRemote;
  60. }
  61. public java.lang.String getInterfaceDescriptor() {
  62. return DESCRIPTOR;
  63. }
  64. @Override
  65. public java.lang.String getName() throws android.os.RemoteException {
  66. android.os.Parcel _data = android.os.Parcel.obtain();
  67. android.os.Parcel _reply = android.os.Parcel.obtain();
  68. java.lang.String _result;
  69. try {
  70. _data.writeInterfaceToken(DESCRIPTOR);
  71. mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
  72. _reply.readException();
  73. _result = _reply.readString();
  74. } finally {
  75. _reply.recycle();
  76. _data.recycle();
  77. }
  78. return _result;
  79. }
  80. }
  81. static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  82. }
  83. public java.lang.String getName() throws android.os.RemoteException;
  84. }
  1. Intent tIntent = new Intent("io.github.trylovecatch.AIDL_SERVICE");
  2. tIntent.setPackage("io.github.trylovecatch.aidlserver");
  3. mServiceConnection = new ServiceConnection() {
  4. @Override
  5. public void onServiceConnected(ComponentName name, IBinder service) {
  6. mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
  7. }
  8. @Override
  9. public void onServiceDisconnected(ComponentName name) {
  10. }
  11. };
  12. bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
  1. @Override
  2. public IBinder onBind(Intent intent) {
  3. return new MyBinder();
  4. }
  5. class MyBinder extends IMyAidlInterface.Stub {
  6. @Override
  7. public String getName() throws RemoteException
  8. {
  9. return "哈哈哈";
  10. }
  11. }
  1. public Stub() {
  2. this.attachInterface(this, DESCRIPTOR);
  3. }

DESCRIPTOR和实现了IInterfaceIMyAidlInterface作为(key, value)存在MyBinder对象里面。

  1. IMyAidlInterface.Stub.asInterface(service)

我们看一下asInterface()

  1. public static io.github.trylovecatch.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {
  2. if ((obj==null)) {
  3. return null;
  4. }
  5. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  6. if (((iin!=null)&&(iin instanceof io.github.trylovecatch.aidlserver.IMyAidlInterface))) {
  7. return ((io.github.trylovecatch.aidlserver.IMyAidlInterface)iin);
  8. }
  9. return new io.github.trylovecatch.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
  10. }

这个queryLocalInterface(),就是根据DESCRIPTOR去查找已经保存的IInterface对象,但是上面提过,这个add(key, value)的过程是在Stub的构造函数里面,它是在Server端完成的,所以这里肯定返回null,所以肯定返回的是Stub.Proxy对象。

  1. mAidlInterface.getName()

这个getName()方法,是调用Proxy里面的方法:

  1. @Override
  2. public java.lang.String getName() 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. mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
  10. _reply.readException();
  11. _result = _reply.readString();
  12. }
  13. finally {
  14. _reply.recycle();
  15. _data.recycle();
  16. }
  17. return _result;
  18. }

这里面主要,调用了mRemote.transact(),传递数据给Binder驱动,我们知道这个mRemoteBindProxy对象,调用这个方法后,Client线程会挂起。

  1. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  2. {
  3. switch (code)
  4. {
  5. case INTERFACE_TRANSACTION:
  6. {
  7. reply.writeString(DESCRIPTOR);
  8. return true;
  9. }
  10. case TRANSACTION_getName:
  11. {
  12. data.enforceInterface(DESCRIPTOR);
  13. java.lang.String _result = this.getName();
  14. reply.writeNoException();
  15. reply.writeString(_result);
  16. return true;
  17. }
  18. }
  19. return super.onTransact(code, data, reply, flags);
  20. }

调用this.getName(),也就是StubgetName(),实际上就是调用了MyBindergetName()

  1. @Override
  2. public String getName() throws RemoteException
  3. {
  4. return "哈哈哈";
  5. }

返回结果给Binder驱动

拾遗一

Messenger

总结

例子

Service

  1. public class MessengerService extends Service {
  2. private Handler mHandler = new Handler(){
  3. @Override
  4. public void handleMessage(Message msg) {
  5. Messenger tClient = null;
  6. switch (msg.what){
  7. case WHAT_FROM_CLIENT_CONNECTED:
  8. Log.e("service", "message from client: " + msg.getData().getString("msg"));
  9. tClient = msg.replyTo;
  10. if(tClient!=null){
  11. reply(tClient, "链接成功,哈哈哈");
  12. }
  13. break;
  14. case WHAT_FROM_CLIENT_CLICK_BTN:
  15. Log.e("service", "message from client: " + msg.getData().getString("msg"));
  16. tClient = msg.replyTo;
  17. if(tClient!=null){
  18. reply(tClient, "我知道你点击了按钮");
  19. }
  20. break;
  21. default:
  22. super.handleMessage(msg);
  23. break;
  24. }
  25. }
  26. };
  27. private final Messenger mMessenger = new Messenger(mHandler);
  28. @Nullable
  29. @Override
  30. public IBinder onBind(Intent intent) {
  31. return mMessenger.getBinder();
  32. }
  33. private void reply(Messenger pClient, String tMsg){
  34. Message tMessage = Message.obtain(null, WHAT_FROM_SERVICE);
  35. Bundle tBundle = new Bundle();
  36. tBundle.putString("reply", tMsg);
  37. tMessage.setData(tBundle);
  38. try {
  39. pClient.send(tMessage);
  40. } catch (RemoteException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. }

Manifest.xml

  1. <service android:name=".MessengerService" android:process=":remote" />

Client

  1. private Handler mHandler = new Handler(){
  2. @Override
  3. public void handleMessage(Message msg) {
  4. switch (msg.what){
  5. case WHAT_FROM_SERVICE:
  6. Log.e("client", "receive msg from service: " + msg.getData().getString("reply"));
  7. break;
  8. default:
  9. super.handleMessage(msg);
  10. break;
  11. }
  12. }
  13. };
  14. private final Messenger mMessengerClient = new Messenger(mHandler);
  15. private Messenger mMessengerService;
  16. private ServiceConnection mServiceConnection = new ServiceConnection() {
  17. @Override
  18. public void onServiceConnected(ComponentName name, IBinder service) {
  19. mMessengerService = new Messenger(service);
  20. send(WHAT_FROM_CLIENT_CONNECTED, "hello, hahahaha");
  21. }
  22. @Override
  23. public void onServiceDisconnected(ComponentName name) {
  24. }
  25. };
  26. @Override
  27. protected void onCreate(Bundle savedInstanceState) {
  28. super.onCreate(savedInstanceState);
  29. setContentView(R.layout.activity_main);
  30. Intent tIntent = new Intent(this, MessengerService.class);
  31. bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
  32. findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
  33. @Override
  34. public void onClick(View v) {
  35. send(WHAT_FROM_CLIENT_CLICK_BTN, "click client btn");
  36. }
  37. });
  38. }
  39. @Override
  40. protected void onDestroy() {
  41. super.onDestroy();
  42. unbindService(mServiceConnection);
  43. }
  44. private void send(int pWhat, String tMsg){
  45. if(mMessengerService !=null){
  46. Message tMessage = Message.obtain(null, pWhat);
  47. Bundle tData = new Bundle();
  48. tData.putString("msg", tMsg);
  49. tMessage.setData(tData);
  50. tMessage.replyTo = mMessengerClient;
  51. try {
  52. mMessengerService.send(tMessage);
  53. } catch (RemoteException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. }

代码就这么些,比较简单,源码可以参考:这里
Messenger的工作原理如下图:

拾遗二

拾遗三 RemoteCallbackList & IBinder.DeathRecipientIBinder.DeathRecipient

https://www.jianshu.com/p/69e5782dd3c3

DeathRecipient类的作用,当Binder服务端程序挂掉了,通知给Binder客户端程序
在某些场景下,Binder服务端需要回调每一个Binder代理对象对应的回调接口,要先确保Binder代理对象没有死亡,才发送。RemoteCallbackList内部是通过DeathRecipient和匿名Binder来实现的。

拾遗三 传输大文件

ParcelFileDescriptor
MemoryFile

总得来说 ParcelFileDescriptor 和 MemoryFile 的区别有以下几点:

在其他领域的应用方面,ParcelFileDescriptor和MemoryFile也有着性能上的差异,主要取决于两个方面:

参考

AIDL使用

Android中AIDL的使用详解
Android Binder Analysis(2)

Binder机制

Binder学习指南
User space 与 Kernel space
图文详解 Android Binder跨进程通信机制 原理

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注