概述:
Binder通信涉及的源码纷繁复杂,细节更是深得难以细究,本文试图以简洁的语言说明binder原理,以让读者理解binder大致框架和流程,至于具体的细节这里不作陈述,需要读者自己研读源码了.
由于java层的binder只是对c++层binder的封装调用,要想真正理解binder还是要从c++层分析,因此本文所涉及的代码均是c++层.文章最后也会贴个小demo
首先,binder是什么?
简单来说binder就是一套通信协议,可以和网络通信类比.
Binder涉及到的几个重要角色:
1.servicemanger----类比dns服务器
2.service---类比具体的网络服务提供方,如百度服务器
3.client---类比个人电脑
4.handle ---类比ip地址
至于BpBinder,BBinder,BpInterface,BnInterface这里先不说
我们来看看完整的binder通信流程是什么样的(和网络通信类比)
1.dns服务器先要启动
----servicemanager先启动
2.百度服务器向dns服务器传送自己的ip地址(如1.1.1.1)
--- service向servicemanager注册自己的句柄值
3.个人电脑向dns查询百度www.baidu.com的ip地址
-- -client通过service名称向servicemager获取service的句柄值
4.个人电脑拿到ip地址后就和百度服务器通信了
---client通过拿到的句柄值和service通信
这里有一点要说明,为什么百度和个人电脑可以和dns直接通信?因为dns有固定的且众所周知的IP如google的8.8.8.8,国内的114.114.114.114
同样service和client可以和servicemanager通信的原因是,servicemanager也有自己固定句柄值0 (servciemanager本身也是一个服务).
在开始之前先说明一点,client和service都是运行在用户态,而binder驱动是运行在内核态;运行在用户态的进程不能直接交换数据,所以要通过运行在内核态的binder驱动来交换数据进行通信.
一.首先说说service注册
servicemanager启动不是本文重点,先从service向servicemanger注册开始.话不多说,直接上时序图
service通过内核和servicemanager建立通信,并向servicemanger注册自己对应的句柄值.
图示中有几个红色标注的注意事项要说明:
1.注册服务的代码经常是这样的
sp<IServiceManager> sm = defaultServiceManager();
sm->addService(String16("service.testservice"), new BnTestService());
defaultServiceManager()最终获取new BpServiceManager(new BpBinder(0))
所以sm->addService()就是BpServiceManager->addService();
addServic()参数”service.testservice”是服务的名字,可以类比理解成”www.baidu.com”
2.内核中ioctl()函数会把请求过来的所有进程封装成binder_proc结构体,并将这些结构体使用双向链表的形式串联起来,以便管理各个进程
3.内核binder_transaction()函数会在service对应的binder_proc中新建binder_node,在servicemanager对应的binder_proc建立binder_ref,并让binder_ref指向binder_node让这两个进程保持关联.
最后唤醒servicemanager对应的线程
4.servicemanager对应的线程被唤醒后,执行自己的binder_thread_read()
binder_thread_read()向用户空间传送BR_TRANSACTION语义,告诉servicemanager数据已准备好,你可以工作了.
5.此时servicemanager可以解析传输过来的数据了.
注意servicemanger中的binder.c和内核binder.c文件不是同一个文件
前者位于frameworks/native/cmds/servicemanager/binder.c
后者位于kernel/androidxxx/drivers/android/binder.c
6.servicemanager将service对应的binder句柄值储存在单链表中.
二. 再看看client如何获取service对应的binder句柄值
时序图如下
client通过内核和servicemanager建立通信,并从servicemanger中获取service对应的binder句柄值.
红色标注注意点说明:
1.client获取服务的代码经常是这样的
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("service.testservice"));
关于sm是什么前文已有说明,sm->getService()就是BpServiceManager->getervice();
这里获取的binder就是BpBinder
2.在service回复client阶段,内核找到service句柄值后,client对应的binder_proc中创建binder_ref,并指向service对应的binder_proc的binder_node,使client和service产生联系,为二者后续通信打下基础.
binder_proc是内核创建的对应用户空间进程的结构体,前文有说,这里再提一下.
3.client拿到service对应的句柄值后,使用该句柄构建新的BpBinder. 后续就可以通过该BpBinder和service通信了
三. client和service通信
时序图如下
client终于可以和service通信了,下面就标注的红色注意事项做一一说明
1.client一般不直接使用BpBinder,而是通过自定义一个继承BpInterface的子类如BpTestService来使用;
BpInterface父类的成员变量mRemote就是BpBinder,这样就可以通过: 自定义的BpTestService--->BpInterface--->父类mRemote 这样的调用链来使用BpBinder了.
而BpBinder正集成了前面client获取到的service对应的binder句柄值,这样就有了通信的基础.
2.binder_transaction()
在内核中通过client对应的binder_proc.binder_ref 找到servcie对应的binder_proc.binder_node. 这样就找到了servcie进程
3.service端一般也会自定义一个继承BnInterface并重写onTransact()方法的子类,用来处理client的请求.
4.在service回复client阶段,内核在线程事务栈里找到发起端的线程,从而找到发起端的进程,也就是client进程.
5.从内核写回的数据已经存储到用户空间的Parcel,这个时候client就可以从Parcel中读取读取servcie回复的相关数据了
至此,service注册handle, client获取handle, client和servcie通信大体框架已经讲完,最后附上c++层client和service通信demo
1.BnTestService.h
继承BnInterface, 主要实现onTrasact(),处理client端过来的调用
/**
* BnTestService.h
*
* BnInterface:
* 与BpTestServcie相对应, 继承BBinder
*
* BnTestService:
* 一般情况下,服务端并不直接使用BnInterface<>,
* 而是使用新的子类继承它,并重写onTransact()方法
*
*/
#include "ITestService.h"
namespace android
{
class BnTestService: public BnInterface<ITestService>
{
public:
//重写BBinder.onTransact()
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply ,uint32_t flags = 0);
/**
* 继承了ITestService,就必须重写其纯虚拟函数
* 服务端重写该方法,表示服务端的处理逻辑,将处理结果返回给客户端
*/
virtual int test(int x) ;
};
int BnTestService::test(int x)
{
return x*2;
}
//服务端收到数据
status_t BnTestService::onTransact(uint_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{
switch (code)
{
case TRANSACTION_test:
printf("server, BnTestService::onTransact, code: TEST\n");
CHECK_INTERFACE(ITest, data, reply);
int arg0 = data.readInt32();
int ret = test(arg0);
printf("server called arg0=%d, send=%d\n", arg0, ret);
reply->writeInt32(ret); //回复client端
return NO_ERROR;
break;
}
return NO_ERROR;
}
}// namespace android
2.BpTestService.h
继承BpInterface, 封装BpBinder了,方便客户端调用
/**
* BpTestService.h
*
* client进程并不直接和BpBinder(Binder代理)通信,而是通过BpInterface的成员函数完成远程调用
*
* BpInterface:
* 使用了模板技术,而且因为它继承了BpRefBase,所以先天上就聚合了一个mRemote成员,
* 这个成员记录的就是BpBinder对象
*
* BpTestService:
* 继承BpInterface<>,是我们实现的代理类。
*
*/
#include "ITestService.h"
namespace android
{
class BpTestService: public BpInterface<ITestService>
{
public:
BpTestService(const sp<IBinder>& impl);
virtual int test(int x);
};
BpTestService::BpTestService(const sp<IBinder> &impl)
: BpInterface<ITestService>(impl) {}
int BpTestService::test(int x)
{
printf("client, 准备发送数据---> %d\n",x);
Parcel data, reply;
data.writeInterfaceToken(ITestService::getInterfaceDescriptor());
data.writeInt32(x);
remote()->transact(TRANSACTION_test, data, &reply);
printf("client, reply: %d\n", reply.readInt32());
return reply.readInt32();
}
}
3.ITestService.cpp
辅助接口.
/**
*ITestService.cpp
*
*/
#include "ITestService.h"
#include "BpTestService.h"
namespace android
{
//对宏DECLARE_META_INTERFACE的实现; 主要实现asInterface()
IMPLEMENT_META_INTERFACE(TestService, "android.TestServer.ITestService");
} // namespace android
4.ITestService.h
辅助接口。
提供client和service共同的方法标识TRANSACTION_test,这样service收到client的数据就知道client要调用哪个函数
/**
* ITestService.h
*
*/
#ifndef __TEST_H__
#define __TEST_H__
#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
using namespace android;
namespace android
{
class ITestService : public IInterface
{
public:
//该宏主要定义和ITestService相关的方法
//如:android::sp<ITestService> asInterface(const android::sp<android::IBinder>& obj);
//如:ITestService()
DECLARE_META_INTERFACE(TestService);
/**
* 声明一个函数给客户端和服务端使用
* 客户端在BpTestService::test()实现,对远程binder封装,以便客户端调用
* 服务端在BnTestService::test()实现,处理具体实现逻辑
*/
virtual int test(int x)=0;
};
enum
{ //服务端提供方法的标识. 服务端可以提供很多方法,客户端想调用哪个就要传对应标识
TRANSACTION_test = IBinder::FIRST_CALL_TRANSACTION + 0,
};
}
#endif
5.client 端入口
/**
* TestClient.cpp
*/
#include "ITestService.h"
int main()
{
//返回new BpServiceManager(new BpBinder(0));
sp<IServiceManager> sm = defaultServiceManager();
//binder是new BpBinder(handle), handle从service_manager获取
sp<IBinder> binder = sm->getService(String16("service.testservice"));
//cs 是new BpTestService(binder)
sp<ITestService> cs = interface_cast<ITestService>(binder);
int ret = cs->test(3);
printf("ret = %d\n", ret);
return 0;
}
6.service端入口
/**
* TestServer.cpp
*/
#include "ITestService.h"
#include "BnTestService.h"
int main() {
sp<ProcessState> proc(ProcessState::self());
//返回new BpServiceManager(new BpBinder(0))
sp<IServiceManager> sm = defaultServiceManager();
//BnTestService间接继承BBinder
sm->addService(String16("service.testservice"), new BnTestService());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
#Android.mk
LOCAL_PATH := $(call my-dir)
#生成binder service的服务端
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder
LOCAL_MODULE := TestServer
LOCAL_SRC_FILES := \
TestServer.cpp \
ITestService.cpp
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
#生成binder service的测试client端
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder
LOCAL_MODULE := TestClient
LOCAL_SRC_FILES := \
TestClient.cpp \
ITestService.cpp
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)