在Android系统中,进程间的通信(IPC)是一个重要的机制,它允许不同的应用程序或系统服务在相互隔离的内存空间中交换信息和数据。而Binder,作为Android提供的一种独特的IPC实现,不仅高效而且易于使用。本文将详细介绍Binder是什么、为什么使用Binder、Binder的工作原理以及如何在项目中使用Binder进行跨进程通信。
一、Binder是什么?
Binder在Android中扮演着多重角色:
- 跨进程的通信机制:Binder允许不同的进程之间进行数据交换和远程调用,是Android IPC的核心。
- 驱动:Binder底层依赖于Linux内核的一个特殊驱动,这个驱动在内核空间处理进程间的数据传递。
-
接口实现:在Android上层,Binder通过实现
IBinder接口的对象来提供跨进程通信的能力。
二、为什么使用Binder?
Android选择Binder作为IPC机制的原因主要有以下几点:
- 高效的内存管理:进程间通信往往涉及数据的拷贝。传统的IPC机制如Socket需要进行两次数据拷贝(用户空间到内核空间,再从内核空间到另一个用户空间),而Binder只需要一次拷贝(用户空间到内核空间),因为Binder驱动会开辟一块共享的物理内存来存储数据,服务端可以直接通过映射关系访问这块内存,省去了第二次拷贝。
- 基于C/S架构的易用性:Binder基于客户端-服务端(C/S)架构,使得开发更加直观和简单。客户端只需绑定服务端,就可以像调用本地方法一样调用服务端的方法。
- 安全性:Binder为每个进程分配一个唯一的UID,通过身份验证和权限控制来确保通信的安全性。
- 与Android系统的深度集成:作为Android系统的核心组件,Binder与系统的其他部分(如ServiceManager、ActivityManagerService等)紧密集成,提供了强大的功能和稳定性。
客户端通过copy_from_uer将数据拷贝到内核空间,binder驱动会开辟一块共享的物理内存去存储拷贝过来的数据然后通知服务端,服务端收到通知以后会通过mmap方法去物理内存直接拿共享数据。这样的话就不需要二次拷贝,节省了依次copy_to_uer操作。
三、Binder的工作原理
Binder的工作原理涉及多个层面,包括用户空间、内核空间和驱动的实现。以下是Binder工作原理的简要概述:
驱动层:
mmap原理:Linux的机制,它会将一个虚拟内存与磁盘上的物理内存关联起来。当对文件进行mmap操作时,会在进程的虚拟内存分配地址空间创建映射关系,这样就可以采用指针的方式读写操作虚拟内存,而系统也会自动读写到对应的磁盘文件上。
-
binder_init:初始化Binder设备,分配内存,并将其放入binder_devices链表中。 -
binder_open:创建binder_proc对象,表示调用方进程,并将其存入binder_procs链表中。 -
binder_mmap:分配内核虚拟内存和用户空间虚拟内存,并计算它们之间的偏差,然后分配物理内存,并将物理内存与虚拟内存映射到物理内存上。 -
binder_ioctl:用于执行Binder的读写操作。
Binder驱动是Android系统中实现跨进程通信的核心组件,它运行在内核空间,高效管理着客户端与服务端之间的通信。以下是Binder驱动工作机制的精简描述:
Binder驱动负责存储和管理客户端与服务端(如ServiceManager, SM)的Binder对象。通过特定的命令,它能够挂起或唤醒客户端与服务端,实现通信的同步与异步控制。
当客户端发起通信请求时,Binder驱动利用内存映射技术,将服务端与Binder对象的数据存储在同一块物理内存上。这种机制避免了数据的多次拷贝,提高了通信效率。
Binder驱动通过一系列命令来协调通信过程:
-
BC_TRANSACTION:客户端唤醒Binder驱动,发起通信请求。 -
BR_TRANSACTION_COMPLETE:Binder驱动处理完数据后,唤醒服务端,并通知客户端挂起,等待响应。此时,数据仅经过一次拷贝至内核空间。 -
BT_TRANSACTION:Binder驱动通知服务端处理数据。 -
BC_REPLY:服务端处理完数据后,通过Binder驱动通知客户端。 -
BT_REPLY:Binder驱动接收到服务端的通知后,唤醒挂起的客户端。 -
BR_TRANSACTION_COMPLETE(服务端视角):在服务端处理完数据并收到Binder驱动的确认后,服务端挂起,等待下一个请求。
这样的命令序列确保了客户端与服务端之间的通信既高效又可靠,同时保持了数据的完整性和安全性。
ServiceManager注册:
- 通过
binder_open()打开Binder驱动,并设置缓冲区大小。 - 调用
binder_become_context_manager()设置当前进程为守护进程(ServiceManager)。 - 创建
binder_node并插入到链表中。 - 调用
binder_loop()进入循环等待状态,准备处理客户端的请求。
Native层与Java层:
- 在Native层,通过
Binder_open创建Binder对象,并进行内存映射共享空间的操作。使用BpServiceManager和BpBinder来调用服务端的方法。 - 在Java层,通过AIDL(Android Interface Definition Language)定义接口,并生成相应的Stub和Proxy类来实现跨进程通信。
如何在项目中使用Binder?
AILD
在Android项目中,使用Binder进行跨进程通信通常需要通过AIDL来定义接口。以下是使用AIDL进行跨进程通信的基本步骤:
在src/main/java目录下创建一个新的包并在该包下右键点击,选择New -> AIDL -> AIDL File,创建一个新的AIDL文件(例如IMyAidlInterface.aidl)
package com.example.aidldemo;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getName();
int countSum(int a, int b);
}
服务端创建一个新的Service类(例如MyAidlService),并在该类中实现AIDL接口。
package com.example.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyAidlService extends Service {
private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public String getName() throws RemoteException {
return "我是AIDL";
}
@Override
public int countSum(int a, int b) throws RemoteException {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
服务端开启一个服务并通过onBind返回一个Ibinder对象。而Ibinder对象Stub则是AIDL内部帮我们创建的一个封装类。在Stub内部主要实现了onTransact方法,该方法内识别传递的方法名并调用服务端对用的方法,接受Parcel类型的data数据并返回Parcel类型的reply数据从而实现跨进程数据通信。
在AndroidManifest.xml文件中配置Service,并设置android:exported为true以允许其他应用访问。
<service
android:name=".MyAidlService"
android:exported="true">
<intent-filter>
<action android:name="com.example.aidldemo.MyAidlService" />
</intent-filter>
</service>
在客户端Activity中(例如MainActivity),绑定Service并调用AIDL接口。
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
String name = mIMyAidlInterface.getName();
int sum = mIMyAidlInterface.countSum(9, 1);
Toast.makeText(MainActivity.this, "Name: " + name + ", Sum: " + sum, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.aidldemo", "com.example.aidldemo.MyAidlService"));
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
在客户端,用户在连接服务段以后调用 IMyAidlInterface.Stub.asInterface(service);方法获取Binder对象执行对应方法。其中Stub则是AIDL中为我们封装的Binder类,该类返回一个Binder代理对象Proxy,在Proxy中执行对应方法最后会执行mRemote.transact方法去执行服务端对应方法并挂起当前任务。执行流程为:Client->Stub->Proxy(mRemote)->transact方法->BinderProxy->BPbinder->调用服务端。
总结
Binder作为Android提供的IPC机制,以其高效、易用和安全的特点,在Android系统中发挥着重要的作用。通过深入理解Binder的工作原理和在项目中的应用,我们可以更好地利用这一机制来实现进程间的通信和数据交换。希望本文对你有所帮助,如果你有任何问题或建议,请随时留言。