@TryLoveCatch
2023-05-04T09:32:35.000000Z
字数 14462
阅读 2025
Android知识体系
一篇文章了解相见恨晚的 Android Binder 进程间通讯机制
在android studio里面使用AIDL,我们直接在Server工程目录右键,新建AIDL文件,如下图:

修改IMyAidlInterface.aidl
interface IMyAidlInterface {String getName();}
这个就是我们这个Server提供的功能。
IMyAidlInterface.aidl对应的java文件,点击Make Project(下图中的小锤子) 
在app->build->generated->source->aidl->debug路径下面生成了IMyAidlInterface.java。
public class MyService extends Service{@Overridepublic void onCreate() {super.onCreate();}@Nullable@Overridepublic IBinder onBind(Intent intent) {// 检查权限int tCheck = checkCallingOrSelfPermission("io.github.trylovecatch.AIDL_SERVICE");if(tCheck == PackageManager.PERMISSION_DENIED){return null;}return new MyBinder();}class MyBinder extends IMyAidlInterface.Stub{@Overridepublic String getName() throws RemoteException{return "哈哈哈";}}}
Manifest文件
<permission android:protectionLevel="normal" android:name="io.github.trylovecatch.AIDL_SERVICE"/>
注册MyService
<serviceandroid:name=".MyService"android:enabled="true"android:exported="true"><intent-filter><action android:name="io.github.trylovecatch.AIDL_SERVICE" /></intent-filter></service>
好了,Server端到此,就全部结束了。
IMyAidlInterface.aidl文件到Client工程,保证包名一直。Make Project,同样会生成一个java文件。
Intent tIntent = new Intent("io.github.trylovecatch.AIDL_SERVICE");tIntent.setPackage("io.github.trylovecatch.aidlserver");mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mAidlInterface = IMyAidlInterface.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {}};bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {try {Toast.makeText(MainActivity.this, mAidlInterface.getName(), Toast.LENGTH_LONG).show();} catch (RemoteException e) {e.printStackTrace();}}});@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mServiceConnection);}
Manifest
<uses-permission android:name="io.github.trylovecatch.AIDL_SERVICE"/>
整个过程就是这样,当点击Button的时候,就会Toast显示哈哈哈,证明远程调用成功。
代码下载: 源代码
进程隔离是为保护操作系统中进程互不干扰而设计的一组不同硬件和软件的技术。这个技术是为了避免进程A写入进程B的情况发生。 进程的隔离实现,使用了虚拟地址空间。进程A的虚拟地址和进程B的虚拟地址不同,这样就防止进程A将数据信息写入进程B。
操作系统的不同进程之间,数据不共享;对于每个进程来说,它都天真地以为自己独享了整个系统,完全不知道其他进程的存在;因此一个进程需要与另外一个进程通信,需要某种系统机制才能完成。
首先,我们说下Linux Kernel:
它是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。
内核空间
Linux Kernel的运行空间,就是Kernel space(内核空间)。
Kernel space可以执行任意命令,调用系统的一切资源。
用户空间
用户程序的运行空间,就是
User space(用户空间)。
User space 只能执行简单的运算,不能直接调用系统资源,必须通过系统调用(system call),才能向内核发出指令。
为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。这样就对Linux Kernel起到了保护作用。
举个例子:
str = "my string" // 用户空间x = x + 2file.write(str) // 切换到内核空间y = x + 4 // 切换回用户空间
第三行需要写入文件,就要切换到 Kernel space,因为用户不能直接写文件,必须通过内核安排
虽然从逻辑上抽离出用户空间和内核空间;但是不可避免的的是,总有那么一些用户空间需要访问内核的资源;比如应用程序访问文件,网络是很常见的事情,怎么办呢?
上面我们说过了
用户空间访问内核空间的唯一方式就是
系统调用(system call)
通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。用户软件良莠不齐,要是它们乱搞把系统玩坏了怎么办?因此对于某些特权操作必须交给安全可靠的内核来执行。
内核运行态(内核态),指一个任务(进程)执行系统调用而陷入内核代码中时。例如上面例子的第三行。
用户运行态(用户态),当进程在执行用户自己的代码时。例如上面例子的第一、二、四行。
通过系统调用,用户空间可以访问内核空间,那么如果一个用户空间想与另外一个用户空间进行通信怎么办呢?
我们希望,最好操作系统内核能够支持,事实上,确实是支持的:传统的Linux通信机制,比如Socket,管道等都是内核支持的。
但是,Binder并不是Linux 内核的一部分,它是怎么做到访问内核空间的呢?
答案就是:动态可加载内核模块(Loadable Kernel Module,LKM)机制。
LKM是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行。
这样,Android系统可以通过添加一个内核模块运行在内核空间,用户空间之间可以通过这个模块作为桥梁,来完成通信了。
在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动;它是一种虚拟的物理设备,位于/dev/binder路径下。
Linux进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造;而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性.通过上面的介绍,我们知道:
用户空间A要和用户空间B通信,必须借助内核空间的支持,而Android通过LKM,添加了一个内核模块到内核空间,这个内核模块,就是Binder驱动。
我们举一个现实中的例子,来说明这个过程。
假设A和B要进行通信,通信的媒介是打电话(A是Client,B是Server)。A要给B通信:
B把自己写入通讯录里面A通过通讯录得到B的电话A拨打电话,通过基站和B建立链接A和B通信这里面有四个角色:
1. B,Server,提供服务,并把自己写入通讯录里面
2. A,Client,发起通信的,需要B的服务
3. 通讯录,保存了Server的对应关系,叫做ServiceManager(SM)
4. 基站,类似于Binder驱动,通过它的帮助,才能彼此联系上

整个通信步骤如下:
SM建立(建立通信录);首先有一个进程向驱动提出申请为SM;驱动同意之后,SM进程负责管理Service。不过这时候通信录还是空的,一个号码都没有。Server向SM注册(完善通信录);每个Server端进程启动之后,向SM报告,我是zhangsan,要找我请返回0x1234(这个地址没有实际意义,类比);其他Server进程依次如此;这样SM就建立了一张表,对应着各个Server的名字和地址。Client想要与Server通信,首先询问SM;请告诉我如何联系zhangsan,SM收到后给他一个号码0x1234;Client收到之后,开心滴用这个号码拨通了Server的电话,于是就开始通信了。这里Client与SM的通信,以及Client与Server的通信,都会经过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:
package io.github.trylovecatch.aidlserver;// Declare any non-default types here with import statementspublic interface IMyAidlInterface extends android.os.IInterface {/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binderimplements io.github.trylovecatch.aidlserver.IMyAidlInterface {private static final java.lang.String DESCRIPTOR = "io.github.trylovecatch.aidlserver.IMyAidlInterface";/*** Construct the stub at attach it to the interface.*/public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an io.github.trylovecatch.aidlserver.IMyAidlInterface interface,* generating a proxy if needed.*/public static io.github.trylovecatch.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof io.github.trylovecatch.aidlserver.IMyAidlInterface))) {return ((io.github.trylovecatch.aidlserver.IMyAidlInterface) iin);}return new io.github.trylovecatch.aidlserver.IMyAidlInterface.Stub.Proxy(obj);}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_getName: {data.enforceInterface(DESCRIPTOR);java.lang.String _result = this.getName();reply.writeNoException();reply.writeString(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements io.github.trylovecatch.aidlserver.IMyAidlInterface {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.lang.String getName() throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.lang.String _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);_reply.readException();_result = _reply.readString();} finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public java.lang.String getName() throws android.os.RemoteException;}
Client调用bindService()
Intent tIntent = new Intent("io.github.trylovecatch.AIDL_SERVICE");tIntent.setPackage("io.github.trylovecatch.aidlserver");mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mAidlInterface = IMyAidlInterface.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {}};bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);
IBinder对象不存在,Server端会回调onBind()。如果存在,直接回调ServiceConnection的onServiceConnected,并传入IBinder对象。
@Overridepublic IBinder onBind(Intent intent) {return new MyBinder();}class MyBinder extends IMyAidlInterface.Stub {@Overridepublic String getName() throws RemoteException{return "哈哈哈";}}
onBind()里面新建了MyBinder对象(注意,这个新建不一定必须在onBinder()里面,可以是成员变量的),而MyBinder继承 IMyAidlInterface.Stub。首先会调用:
public Stub() {this.attachInterface(this, DESCRIPTOR);}
将DESCRIPTOR和实现了IInterface的IMyAidlInterface作为(key, value)存在MyBinder对象里面。
new MyBinder()这个过程时,Server进程 通过Binder驱动 向 Service Manager进程 注册服务。在这个绑定的时候,Binder驱动会通过binder_node的数据结构保存Binder本地对象,也就是这个MyBinder的对象;而SM,会保存一个Binder的对应关系Client的bindService(),会将Server的信息,如action、package等信息,传递个Binder驱动;Binder驱动再将请求传递给SM,SM根据上一步保存的对应信息,得到所需要的Server和Binder;Binder驱动程序根据SM返回的信息,得到对应的Binder本地对象,但是不会立马返回,而是返回了一个代理对象——BinderProxy,用binder_ref的数据结构存储。Client,回调ServiceConnection的onServiceConnected,传进来一个IBinder对象(如果同一个进程,就是Binder对象,如果不同进程,就是BinderProxy),调用:
IMyAidlInterface.Stub.asInterface(service)
我们看一下asInterface():
public static io.github.trylovecatch.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof io.github.trylovecatch.aidlserver.IMyAidlInterface))) {return ((io.github.trylovecatch.aidlserver.IMyAidlInterface)iin);}return new io.github.trylovecatch.aidlserver.IMyAidlInterface.Stub.Proxy(obj);}
这个queryLocalInterface(),就是根据DESCRIPTOR去查找已经保存的IInterface对象,但是上面提过,这个add(key, value)的过程是在Stub的构造函数里面,它是在Server端完成的,所以这里肯定返回null,所以肯定返回的是Stub.Proxy对象。
Client端,拿到了Proxy对象,然后可以调用Server端的提供的功能了
mAidlInterface.getName()
这个getName()方法,是调用Proxy里面的方法:
@Overridepublic java.lang.String getName() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.lang.String _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);_reply.readException();_result = _reply.readString();}finally {_reply.recycle();_data.recycle();}return _result;}
这里面主要,调用了mRemote.transact(),传递数据给Binder驱动,我们知道这个mRemote是BindProxy对象,调用这个方法后,Client线程会挂起。
Binder驱动,根据BindProxy对象,找到对应的Binder本地对象,传递数据给它,并且回调Binder对象的onTransact()Server端,被执行了onTransact(),如下:
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{switch (code){case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_getName:{data.enforceInterface(DESCRIPTOR);java.lang.String _result = this.getName();reply.writeNoException();reply.writeString(_result);return true;}}return super.onTransact(code, data, reply, flags);}
调用this.getName(),也就是Stub的getName(),实际上就是调用了MyBinder的getName():
@Overridepublic String getName() throws RemoteException{return "哈哈哈";}
返回结果给Binder驱动。
Binder驱动,再把数据返回给Client端,Client端线程被唤醒,显示结果。Messenger
Messenger,可以在不同进程间传递Message对象,来实现进程间通信。Messenger的底层是AIDL实现的,它是一种轻量级的实现,不需要编写AIDL文件。Messenger以串行的方式处理客户端发来的信息,如果大量的信息发送给服务器,服务器仍然只能一个个来处理,所以Messenger不适用大量的并发请求。Messenger主要作用是传递数据,只能传递Message对象;所以,不适合哪些需要跨进程调用服务器端方法的场景。Service
public class MessengerService extends Service {private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {Messenger tClient = null;switch (msg.what){case WHAT_FROM_CLIENT_CONNECTED:Log.e("service", "message from client: " + msg.getData().getString("msg"));tClient = msg.replyTo;if(tClient!=null){reply(tClient, "链接成功,哈哈哈");}break;case WHAT_FROM_CLIENT_CLICK_BTN:Log.e("service", "message from client: " + msg.getData().getString("msg"));tClient = msg.replyTo;if(tClient!=null){reply(tClient, "我知道你点击了按钮");}break;default:super.handleMessage(msg);break;}}};private final Messenger mMessenger = new Messenger(mHandler);@Nullable@Overridepublic IBinder onBind(Intent intent) {return mMessenger.getBinder();}private void reply(Messenger pClient, String tMsg){Message tMessage = Message.obtain(null, WHAT_FROM_SERVICE);Bundle tBundle = new Bundle();tBundle.putString("reply", tMsg);tMessage.setData(tBundle);try {pClient.send(tMessage);} catch (RemoteException e) {e.printStackTrace();}}}
Manifest.xml
<service android:name=".MessengerService" android:process=":remote" />
Client
private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what){case WHAT_FROM_SERVICE:Log.e("client", "receive msg from service: " + msg.getData().getString("reply"));break;default:super.handleMessage(msg);break;}}};private final Messenger mMessengerClient = new Messenger(mHandler);private Messenger mMessengerService;private ServiceConnection mServiceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mMessengerService = new Messenger(service);send(WHAT_FROM_CLIENT_CONNECTED, "hello, hahahaha");}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent tIntent = new Intent(this, MessengerService.class);bindService(tIntent, mServiceConnection, BIND_AUTO_CREATE);findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {send(WHAT_FROM_CLIENT_CLICK_BTN, "click client btn");}});}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mServiceConnection);}private void send(int pWhat, String tMsg){if(mMessengerService !=null){Message tMessage = Message.obtain(null, pWhat);Bundle tData = new Bundle();tData.putString("msg", tMsg);tMessage.setData(tData);tMessage.replyTo = mMessengerClient;try {mMessengerService.send(tMessage);} catch (RemoteException e) {e.printStackTrace();}}}
代码就这么些,比较简单,源码可以参考:这里
Messenger的工作原理如下图:


https://www.jianshu.com/p/69e5782dd3c3
DeathRecipient类的作用,当Binder服务端程序挂掉了,通知给Binder客户端程序
在某些场景下,Binder服务端需要回调每一个Binder代理对象对应的回调接口,要先确保Binder代理对象没有死亡,才发送。RemoteCallbackList内部是通过DeathRecipient和匿名Binder来实现的。
ParcelFileDescriptor
MemoryFile
总得来说 ParcelFileDescriptor 和 MemoryFile 的区别有以下几点:
在其他领域的应用方面,ParcelFileDescriptor和MemoryFile也有着性能上的差异,主要取决于两个方面:
数据的大小和类型。
数据的访问方式。
Android中AIDL的使用详解
Android Binder Analysis(2)
Binder学习指南
User space 与 Kernel space
图文详解 Android Binder跨进程通信机制 原理