【Android】Binder机制浅析
1、前言
BInder机制是Android系统提供的跨进程通信机制。本篇文章会从Linux的基础知识开始介绍,从基础概念引出Binder机制,接着分析Binder的通信模型和原理,最后将会手动实现AIDL完成进程间的通信。侧重点在于原理和使用上,适合初学者。
2、Binder原理
Android是基于Linux内核的,Linux提供了管道、消息队列、信号量、内存共享、套接字等跨进程方式,而Android为什么采用了Binder作为主要机制呢?
一、传输性能好
- Socket:是一个通用接口,导致其传输效率低,开销大。
- 共享内存:虽然在传输时不需要拷贝数据,但其控制机制复杂
- Binder:复杂数据类型传递可复用内存,需要拷贝1次数据
- 管道和消息队列:采用存储转发方式,至少需要拷贝2次数据,效率低
二、安全性高
- 传统的进程:通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设
- Binder机制:从协议本身就支持对通信双方做身份校验,因而大大提升了安全性
2.1 Binider实现机制
2.1.1 进程隔离

进程空间划分:用户空间 和内核空间
- 用户空间:表示进程运行在一个特定的操作模式中,没有接触物理内存或设备的权限
- 内核空间:表示独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限
- 每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。例如,对应一个4GB的虚拟内存空间,其中3GB是用户空间,1GB是内核空间,内核空间的大小是可以通过参数配置调整的。
- 对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的。
简单来说,内核空间是系统内核运行的空间,而用户空间是用户程序运行的空间。每个进程都有自己的虚拟内存空间,每个进程只能操作自己的虚拟内存空间,只有操作系统才有权限操作物理内存空间。进程隔离保证了每个进程的内存安全,但在大多情况下,不同进程间的数据通信是不可避免的,因此操作系统必须提供跨进程通信机制。
2.1.2 系统调用
系统调用是用户态切换到内核态的唯一合法途径
抽象来看,操作系统中安全边界的概念就像环路的概念一样,一个环上持有一个特定的权限组,Intel支持四层环,但是Linux只使用了其中的两层环(0号环持有全部权限,3号环持有最少权限,1号环和2号环未使用),系统进程运行在0号环,用户进程运行在3号环。如果一个用户进程需要其他高级权限,其必须从三号环过渡到0号环,过渡需要通过一个安全参数检查的网关,这种过渡被称为系统调用,在执行过程中会产生一定数量的计算开销。所以,用户空间要访问内核空间唯一的方式就是系统调用。

2.1.3 内核模块/驱动
如前面所说,跨进程通信是需要内核空间做支持的。传统的IPC机制如管道、Socket都是内核的一部分。但Binder不是内核的一部分,是如何做到跨进程通信的呢?
Linux的动态内核可加载模块机制解决了这个问题:
- 模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行
- Android系统就可以动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。在Android系统中,这个运行在内核空间中,负责各个用户进程通过Binder实现通信的内核模块就叫Binder驱动
那用户进程之间是如何通过这个内核模块(Binder驱动)来实现通信的呢?这里当然不是从发送方进程拷贝到内核缓存区,再把数据从内核缓存区拷贝到接收方进程,而是通过内存映射:
- 内存映射简单地讲就是把用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间。
- 反之内核空间对这块区域的修改也能直接反映到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。

一次完整的Binder IPC 通信过程通常是这样:
- Binder 驱动在内核空间创建一个数据接收缓存区;
- 然后在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
- 发送方进程通过系统调用 copy_from_user() 将数据拷贝到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
最后,简单总结一下:
- Linux的虚拟内存机制导致内存的隔离,进而导致进程隔离
- 进程隔离的出现导致对内存的操作被划分为用户空间和内核空间
- 用户空间需要跨权限去访问内核空间,必须使用系统调用去实现
- 系统调用需要借助内核模块/驱动去完成
2.2 Binder通信模型
Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及Binder驱动。架构图如下所示:

前三者是在用户空间的,也就是彼此之间无法直接进行交互,Binder驱动是属于内核空间的,属于整个通信的核心。
四个角色:
- Client进程:使用服务的进程。
- Server进程:提供服务的进程。
- ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
- Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
通信原理:
- 注册服务(addService) :Service进程要先注册Service中的Binder对象引用到ServiceManager。在这个过程中,Service是客户端,ServiceManager是服务端。
- 获取服务(getService) :Client进程使用某个Service前,须先向ServiceManager中获取相应的Service的Binder引用。这个过程中,ServiceManager是服务端。
- 使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。这个过程中:Client是客户端,Server是服务端。
概括一下,首先,Service中的Binder实体对象,将自己的引用注册到ServiceManager,Client通过特定的key和这个引用进行绑定,ServiceManager内部自己维护一个类似MAP的表来一一对应,通过这个key就可以在ServiceManager中拿到Service中Binder的引用。
但是这里存在一个问题,ServiceManager和Service也属于两个不同的进程,Service在ServiceManager注册也涉及到进程间通信吗,好像变成无限死循环了?看看下面这张图:

当ServiceManager作为服务端的时候,它提供的Binder比较特殊,它没有名字也不需要注册,它的固定引用号就是0,所有进程都知道ServiceManager的Binder引用号是0。由Binder驱动自动管理,不需要像其他服务那样先注册再使用。这样,一个Service若要向ServiceManager中注册自己的Binder引用就可以通过硬编码的0引用号和ServiceManager中的Binder通信。
那Client怎么拿到Service中的Binder对象呢?图中可以很清晰的看出整个过程,Client原本从ServiceManager中拿到Binder的引用,通过Binder驱动层的处理之后,返回给Client一个代理对象,实际上如果Client和Service处于同一个进程,返回的就是Binder对象。如果不在同一个进程,返回给Client的就是一个代理对象(proxy)。
3、手动实现进程通信
在学习《安卓开发艺术探索》中关于Binder时,首先介绍由AIDL来实现进程间通信,不过相信大多数朋友和我一样,第一次看到系统自动生成的AIDL代码时是懵逼状态的,涉及Stub类,还有Proxy类。这里我们就手动实现一个通信代码,加深对Binder机制的理解。
- 创建Book实体类,使用Parcelable接口实现序列化
java
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
}
- 声明一个AIDL性质的接口,只需要继承IInterface接口即可,IInterface接口中有一个asBinder方法。实现如下:
java
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import java.util.List;
public interface IBookManager extends IInterface {
static final String DESCRIPTOR = "com.example.ipctest.IBookManager";
static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0;
static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
public List<Book> getBookList () throws RemoteException;
public void addBook (Book book) throws RemoteException;
}
在接口中声明了一个Binder描述符和另外两个id,这两个id分别表示的是getBookList和addBook方法。另外,如果再想要添加方法,只需再声明一个id,然后按固定模式声明新方法即可。
- 实现一个Service中的Binder实体对象,继承Binder并实现上面定义好的服务接口。
java
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Collections;
import java.util.List;
public class BookManagerImpl extends Binder implements IBookManager {
public BookManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
List<Book> result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
Book arg0;
if ((0 != data.readInt())) {
arg0 = Book.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
this.addBook(arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
@Override
public List<Book> getBookList() throws RemoteException {
// TODO 待实现
return Collections.emptyList();
}
@Override
public void addBook(Book book) throws RemoteException {
// TODO 待实现
}
}
首先看看asInterface方法,Binder驱动传来的IBinder对象,通过queryLocallnterface方法,查找本地Binder对象,如果返回的就是IBookManager,说明Client和Server处于同一个进程,直接返回,如果不是,返回一个代理对象。
作为代理对象,也要实现服务接口:
ini
private static class Proxy implements IBookManager {
private IBinder mRemote;
public Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List<Book> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Book.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(TRANSACTION_addBook, data, reply, 0);
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
}
这里代理对象实质上就是Client最终拿到的代理服务,通过这个代理对象就可以和Server进行通信了,首先通过Parcel将数据序列化,然后调用remote.transact()将方法code和data传输过去,对应的回调在Server的onTransact()中。
- 然后就是Server进程,onbind方法返回mStub对象,也就是Server中的Binder实体对象。
java
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class MyService extends Service {
private static final String TAG = "MyService";
private List<Book> mBook = new ArrayList<>();
@Override
public void onCreate() {
mBook.add(new Book());
super.onCreate();
}
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
private BookManagerImpl mStub = new BookManagerImpl() {
@Override
public void addBook(Book book) throws RemoteException {
if (book == null) {
book = new Book();
Log.d(TAG, "null obj");
}
mBook.add(book);
Log.d(TAG, mBook.size() + "");
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBook;
}
};
}
- 最后我们在客户端进程,bindService传入一个ServiceConnection对象,在与服务端建立连接时,通过我们定义好的BookManagerImpl的asInterface方法返回一个代理对象,再调用方法进行交互。
ini
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private boolean isConnect = false;
private static final String TAG = "MainActivity";
private IBookManager iBookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
start();
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (iBookManager == null){
Log.d(TAG,"connect error");
return;
}
try {
iBookManager.addBook(new Book());
Log.d(TAG,iBookManager.getBookList().size() + "");
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
}
private void start() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"connect success");
isConnect = true;
iBookManager = BookManagerImpl.asInterface(service);
List<Book> bookList = null;
try {
bookList = iBookManager.getBookList();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
Log.d(TAG,bookList.size() + "");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"connect failed");
isConnect = false;
}
};
}
这样就实现了一次进程间的交互。建议大家手写一遍AIDL进程间通信的实现代码,加深对Binder通信的理解。
内容参考: