1. 跨进程通信(IPC)
在 Linux 中,每个进程都有自己的虚拟内存地址空间,虚拟内存地址空间又分为了用户地址空间和内核地址空间。不同进程之间用户地址空间的变量和函数是不能相互访问的。虽然用户地址空间是不能互相访问的,但是不同进程的内核地址空间是映射到相同物理地址的,它们是相同和共享的,可以借助内核地址空间作为中转站来实现进程间数据的传输。
使得 A 进程能访问到 B 进程中数据的手段就称之为 IPC(Inter-Process Communication)进程间通信,IPC 不是 Android 系统所独有的,任何操作系统都有 IPC 方式,Android 系统基于 Linux 系统而来,Linux 系统提供了多种跨进程通信的方式,eg:管道(Pipe)、信号(Signal)、消息(Message)、共享内存(ShareMemory)和插口(Socket)。
上述跨进程的通信方式,因为通信特点和使用方式的要求,在 Android 系统中并不能满足需求,因此 Android 系统提供了 Binder 跨进程通信方式,Binder 进程间通信机制是在 OpenBinder 的基础上实现的,采用 C/S 通信框架,其中,提供服务的进程称为 Server 进程,而访问服务的进程称为 Client 进程。(OpenBinder:George Hoffman当时任Be公司的工程师,他启动了一个名为OpenBinder的项目,在Be公司被ParmSource公司收购后,OpenBinder由Dinnie Hackborn继续开发,后来成为管理ParmOS6 CobaltOS的进程的基础。在Hackborn加入谷歌后,他在OpenBinder的基础上开发出了Android Binder用来完成Androi的进程通信。)
2.Binder的一次数据拷贝
前面说可以借助内核地址空间作为中转站来实现进程间数据的传输简单示例如下:
在 B 进程使用 copy_from_user 将用户态数据 int a 拷贝到内核态,这样就可以在 A 进程的内核态中访问到 int a
A 进程中调用 copy_to_user 可以将 int a 从内核地址空间拷贝到用户地址空间。至此进程 A 用户态程序就可以访问到进程 B 中的用户地址空间数据 int a 了,为了访问 int a ,需要拷贝两次数据。而Binder通过 mmap 将进程 A 的用户地址空间与内核地址空间进行映射,让他们指向相同的物理地址空间:
完成映射后,B 进程只需调用一次 copy_from_user,A 进程的用户空间中就可以访问到 int a了。这里就优化到了一次拷贝。
3.Binder架构
在Android中我们所使用的Activity,Service等组件都需要和AMS通信,这种跨进程的通信都是通过Binder完成。
机制:Binder是一种进程间通信机制;
驱动:Binder是一个虚拟物理设备驱动; [label color="orange"]/dev/binder[/label]
应用层:Binder是一个能发起通信Java类;
Framework/Native:Binder连接了Client、Server、Service Manager和Binder驱动程序,形成一套C/S的通信架构。
一个完整的 Binder 程序涉及 4 个流程:
- 在 Binder Server 端定义好服务
- 然后向 ServiceManager 注册服务
- 在 Binder Client 中向 ServiceManager 获取到服务
- 发起远程调用,调用 Binder Server 中定义好的服务
整个流程都是建立在 Binder 驱动提供的跨进程调用能力之上:
4.Binder的使用C Demo
4.1Binder 驱动对外提供的使用接口
Binder 是一个字符驱动,对应的设备文件是 /dev/binder,和其他驱动一样,是通过 Linux 的文件访问系统调用对外提供具体功能的:
open(),用于打开 Binder 驱动,返回 Binder 驱动的文件描述符
mmap(),用于在内核中申请一块内存,并完成应用层与内核层的虚拟地址映射
ioctl,在应用层调用 ioctl ,从应用层向内核层发送数据或者读取内核层发送到应用层的数据:
ioctl(文件描述符,ioctl命令,数据)
文件描述符是在调用 open 时的返回值,ioctl 命令和第三个参数"数据"的类型是相关联的,具体如下:
参考https://github.com/weidongshan/APP_0003_Binder_C_App 这个开源项目已经对binder.c 的实现(C语言封装)
ServiceManager 由系统实现,并在系统启动时启动。其源码在 frameworks/native/cmds/servicemanager/ServiceManager.cpp
4.2 Server 端的实现
主要步骤如下:
定义服务,服务就是等待着被远程调用的函数
定义服务回调函数,当我们的 Server 收到远程调用的数据时,会被回调的函数
完成 main 函数流程
Binder 初始化
注册服务
进入 loop, 等待 client 请求服务
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include "binder.h"
#define LOG_TAG "BinderServer"
#include <log/log.h>
#define HELLO_SVR_CMD_SAYHELLO 1
#define HELLO_SVR_CMD_SAYHELLO_TO 2
void sayhello(void)
{
static int cnt = 0;
//fprintf(stderr, "say hello : %d\n", ++cnt);
ALOGW("say hello : %d\n", ++cnt);
}
int sayhello_to(char *name)
{
static int cnt = 0;
//fprintf(stderr, "say hello to %s : %d\n", name, ++cnt);
ALOGW("say hello to %s : %d\n", name, ++cnt);
return cnt;
}
int hello_service_handler(struct binder_state *bs,
struct binder_transaction_data_secctx *txn_secctx,
struct binder_io *msg,
struct binder_io *reply)
{
struct binder_transaction_data *txn = &txn_secctx->transaction_data;
/* 根据txn->code知道要调用哪一个函数
* 如果需要参数, 可以从msg取出
* 如果要返回结果, 可以把结果放入reply
*/
/* sayhello
* sayhello_to
*/
uint16_t *s;
char name[512];
size_t len;
//uint32_t handle;
uint32_t strict_policy;
int i;
// Equivalent to Parcel::enforceInterface(), reading the RPC
// header with the strict mode policy mask and the interface name.
// Note that we ignore the strict_policy and don't propagate it
// further (since we do no outbound RPCs anyway).
strict_policy = bio_get_uint32(msg);
switch(txn->code) {
case HELLO_SVR_CMD_SAYHELLO:
sayhello();
bio_put_uint32(reply, 0); /* no exception */
return 0;
case HELLO_SVR_CMD_SAYHELLO_TO:
/* 从msg里取出字符串 */
s = bio_get_string16(msg, &len); //"IHelloService"
s = bio_get_string16(msg, &len); // name
if (s == NULL) {
return -1;
}
for (i = 0; i < len; i++)
name[i] = s[i];
name[i] = '\0';
/* 处理 */
i = sayhello_to(name);
/* 把结果放入reply */
bio_put_uint32(reply, 0); /* no exception */
bio_put_uint32(reply, i);
break;
default:
fprintf(stderr, "unknown code %d\n", txn->code);
return -1;
}
return 0;
}
int test_server_handler(struct binder_state *bs,
struct binder_transaction_data_secctx *txn_secctx,
struct binder_io *msg,
struct binder_io *reply)
{
struct binder_transaction_data *txn = &txn_secctx->transaction_data;
int (*handler)(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply);
handler = (int (*)(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply))txn->target.ptr;
return handler(bs, txn, msg, reply);
}
int main(int argc, char **argv)
{
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
int ret;
//打开驱动
bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
//添加服务
ret = svcmgr_publish(bs, svcmgr, "hello", hello_service_handler);
if (ret) {
fprintf(stderr, "failed to publish hello service\n");
return -1;
}
binder_loop(bs, test_server_handler);
return 0;
}
4.3 Client 端的实现
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include <stdbool.h>
#include <string.h>
#include "binder.h"
#define HELLO_SVR_CMD_SAYHELLO 1
#define HELLO_SVR_CMD_SAYHELLO_TO 2
int g_handle = 0;
struct binder_state *g_bs;
void sayhello(void)
{
unsigned iodata[512/4];
struct binder_io msg, reply;
/* 构造binder_io */
bio_init(&msg, iodata, sizeof(iodata), 4);
/* 放入参数 */
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, "IHelloService");
/* 调用binder_call */
if (binder_call(g_bs, &msg, &reply, g_handle, HELLO_SVR_CMD_SAYHELLO))
return ;
/* 从reply中解析出返回值 */
binder_done(g_bs, &msg, &reply);
}
int main(int argc, char **argv)
{
int fd;
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
int ret;
bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
g_bs = bs;
/* get service */
g_handle = svcmgr_lookup(bs, svcmgr, "hello");
if (!g_handle) {
return -1;
}
//调用服务
sayhello();
}
5.Binder的使用C++ Demo
使用Android封装好的Binder接口以及Parcel
5.1 数据传递与Parcel的使用
Parcel 是 Android 中用于跨进程数据传输的重要类,它可以将各种数据类型 (基本类型、String、对象、文件描述符等) 序列化到字节流中,并在进程间传递。
Parcel 的常用方法:
写入数据 (write...):
writeBoolean(boolean)
writeByte(byte)
writeInt(int)
writeLong(long)
writeFloat(float)
writeDouble(double)
writeString(String)
writeStrongBinder(IBinder)
writeParcelable(Parcelable, int)
writeFileDescriptor(FileDescriptor)
...
读取数据 (read...):
readBoolean()
readByte()
readInt()
readLong()
readFloat()
readDouble()
readString()
readStrongBinder()
readParcelable(ClassLoader)
readFileDescriptor()
...
在使用 Parcel 进行数据传递时,需要注意以下几点:
写入和读取顺序必须一致: 服务端写入数据的顺序要与客户端读取数据的顺序完全一致,否则会导致数据解析错误。
自定义 Parcelable 对象: 如果需要传递自定义对象,需要实现 Parcelable 接口,并实现 writeToParcel 和 createFromParcel 方法,用于对象的序列化和反序列化。
文件描述符传递: Parcel 可以传递文件描述符,实现进程间共享文件资源。
5.2 Server 端的实现
// calculator_service.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <binder/binder.h> // 需要 NDK 环境,包含 binder 头文件
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
using namespace android;
// 定义 Binder 接口的描述符 (interface token),用于唯一标识接口
static const char* DESCRIPTOR = "demo.calculator";
// 定义 transaction code,用于区分不同的接口方法
enum {
ADD = IBinder::FIRST_CALL_TRANSACTION, // 加法操作
};
// 定义 CalculatorService 本地对象,继承 BBinder
class CalculatorService : public BBinder {
public:
CalculatorService() : BBinder() {
printf("CalculatorService created\n");
}
virtual ~CalculatorService() {
printf("CalculatorService destroyed\n");
}
// 实现 onTransact 方法,处理来自 Binder 驱动的请求
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {
switch (code) {
case ADD: {
// 检查接口描述符
if (!data.checkInterface(this)) {
return UNKNOWN_TRANSACTION;
}
int a = data.readInt32();
int b = data.readInt32();
int result = add(a, b); // 调用本地加法逻辑
reply->writeNoException(); // 写入无异常标志
reply->writeInt32(result); // 写入结果
return NO_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
private:
int add(int a, int b) {
printf("CalculatorService::add(%d, %d) called\n", a, b);
return a + b;
}
};
int main() {
sp<IServiceManager> sm = defaultServiceManager(); // 获取 Service Manager 代理
if (sm == NULL) {
fprintf(stderr, "Failed to get service manager!\n");
return -1;
}
sp<CalculatorService> service = new CalculatorService(); // 创建 CalculatorService 本地对象
// 向 Service Manager 注册服务,服务名为 "calculator_service"
status_t ret = sm->addService(String16("calculator_service"), service);
if (ret != OK) {
fprintf(stderr, "Failed to add service: %d\n", ret);
return -1;
}
printf("CalculatorService registered and running...\n");
// 进入主循环,保持服务运行 (这里简单使用 pause(),实际应用中可能需要更完善的事件循环)
pause();
return 0;
}
5.3 Client 端的实现
// calculator_client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <binder/binder.h> // 需要 NDK 环境,包含 binder 头文件
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
using namespace android;
// 接口描述符,与服务端保持一致
static const char* DESCRIPTOR = "demo.calculator";
// transaction code,与服务端保持一致
enum {
ADD = IBinder::FIRST_CALL_TRANSACTION, // 加法操作
};
int main() {
sp<IServiceManager> sm = defaultServiceManager(); // 获取 Service Manager 代理
if (sm == NULL) {
fprintf(stderr, "Failed to get service manager!\n");
return -1;
}
// 通过服务名 "calculator_service" 查找服务,获取 Binder 代理 (Proxy)
sp<IBinder> binder = sm->getService(String16("calculator_service"));
if (binder == NULL) {
fprintf(stderr, "Failed to get service: calculator_service\n");
return -1;
}
printf("Got binder service: calculator_service\n");
Parcel data, reply;
data.writeInterfaceToken(String16(DESCRIPTOR)); // 写入接口描述符
data.writeInt32(10); // 写入参数 a = 10
data.writeInt32(20); // 写入参数 b = 20
// 发起跨进程调用,transaction code 为 ADD
status_t ret = binder->transact(ADD, data, &reply);
if (ret != OK) {
fprintf(stderr, "transact failed: %d\n", ret);
return -1;
}
int status = reply.readExceptionCode(); // 读取异常代码
if (status != 0) {
fprintf(stderr, "Service call failed with exception: %d\n", status);
return -1;
}
int result = reply.readInt32(); // 读取结果
printf("Result of add(10, 20) is: %d\n", result);
return 0;
}
6.Binder的使用Java Demo
6.1创建 AIDL 接口文件 (ICalculatorService.aidl)
// ICalculatorService.aidl
package demo;
interface ICalculatorService {
/**
* 加法运算
*/
int add(int num1, int num2);//也可以用 Parcelable 类来封装计算请求数据
}
6.2 创建 Service 组件 (CalculatorService.java)
package com.example.aidlbinderdemo.demo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import demo.ICalculatorService; // 导入 AIDL 生成的接口类
public class CalculatorService extends Service {
private static final String TAG = "CalculatorService";
// 实现 AIDL 接口的 Stub 对象
private final ICalculatorService.Stub mBinder = new ICalculatorService.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
Log.d(TAG, "add: num1=" + num1 + ", num2=" + num2);
return num1 + num2;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder; // 返回 Stub 对象
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
6.3在 AndroidManifest.xml 中声明 Service
在 AndroidManifest.xml 文件的 标签内,声明 Service 组件:
<service android:name=".demo.CalculatorService"
android:exported="true" // 允许其他 App 访问
android:process=":calculator_process"> <!-- 指定 Service 运行在独立的进程 -->
</service>
6.4 Client 端代码
在客户端 App 的 src/main/aidl/demo 目录下创建 ICalculatorService.aidl 文件,内容与服务端 App 的 完全相同。
客户端 Activity (MainActivity.java)代码如下:
package com.example.aidlbinderdemo;
import androidx.appcompat.app.AppCompatActivity;
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.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import demo.ICalculatorService; // 导入 AIDL 生成的接口类
public class MainActivity extends AppCompatActivity {
private static final String TAG = "ClientActivity";
private ICalculatorService mCalculatorService; // AIDL 接口实例
private boolean mBound = false; // 标记是否已绑定服务
private EditText num1EditText;
private EditText num2EditText;
private Button addButton;
private TextView resultTextView;
/**
* ServiceConnection 匿名类,用于监听 Service 的连接状态
*/
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// Service 已连接,获取 ICalculatorService 接口实例
mCalculatorService = ICalculatorService.Stub.asInterface(service);
mBound = true;
Log.d(TAG, "Service connected");
Toast.makeText(MainActivity.this, "Calculator Service connected", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
// Service 连接断开
mCalculatorService = null;
mBound = false;
Log.d(TAG, "Service disconnected");
Toast.makeText(MainActivity.this, "Calculator Service disconnected", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 关联布局文件
num1EditText = findViewById(R.id.num1EditText);
num2EditText = findViewById(R.id.num2EditText);
addButton = findViewById(R.id.addButton);
resultTextView = findViewById(R.id.resultTextView);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mBound) {
int num1 = Integer.parseInt(num1EditText.getText().toString());
int num2 = Integer.parseInt(num2EditText.getText().toString());
try {
int result = mCalculatorService.add(num1, num2); // 调用远程服务方法
resultTextView.setText("Result: " + result);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException: ", e);
resultTextView.setText("Error during calculation.");
Toast.makeText(MainActivity.this, "Error communicating with service.", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(MainActivity.this, "Service not connected. Please bind first.", Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
protected void onStart() {
super.onStart();
// 绑定 Service
Intent intent = new Intent();
// 显式 Intent,指定服务端 App 的包名和 Service 类名
intent.setComponent(new ComponentName("com.example.aidlbinderdemo", "com.example.aidlbinderdemo.demo.CalculatorService")); // **重要:修改为服务端 App 的包名和 Service 类名**
bindService(intent, connection, Context.BIND_AUTO_CREATE); // 绑定服务
}
@Override
protected void onStop() {
super.onStop();
// 解绑 Service
if (mBound) {
unbindService(connection);
mBound = false;
Log.d(TAG, "Service unbound");
Toast.makeText(MainActivity.this, "Calculator Service unbound", Toast.LENGTH_SHORT).show();
}
}
}