时间过去了挺久了,直到现在才更新,是遇到了两个问题,一是代码是实际运行的时候报错,排错用了挺长时间,再一个就是一直没有倒出来时间写。
言归正传,现在进行到了Binder的初体验。
Binder的测试代码的编写,主要参考了https://blog.csdn.net/qq_25269161/article/details/86477368这篇帖子。在实际动手的时候遇到了一些理论问题,又主要参考了邓神的深入浅出系列https://www.cnblogs.com/innost/archive/2011/01/09/1931456.html。
根据我的理解,Binder实质上是通过了一个虚拟设备来进行中转,然后是一种CS的模式的IPC通信方式。它的实现是,在客户端和服务端创建两个相同的接口方法,客户端的只是空壳子,而服务端的是具体的实现。当在客户端调用这个方法时,实际上通过binder的转手调用到了服务端的这个方法。在用户层看起来就好像我在客户端的A进程里面直接远程调用了服务端的B进程里面的方法函数似的。
关于binder的原理之后再单独开贴描述,现在还是只重实践。
首先,代码结构如下:
#define LOG_TAG "main_server"
#include<binder/IPCThreadState.h>
#include<binder/ProcessState.h>
#include<binder/IServiceManager.h>
#include<utils/Log.h>
#include"BinderTestService.h"
using namespace android;
int main(/*int argc,char *argv[]*/){
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
BinderTestService::instantiate();//注册进servicemanager中
//开启线程池,接收处理Client发送的进程间通信请求
ProcessState::self()->startThreadPool();
printf("ProcessState startThreadPool\n");
IPCThreadState::self()->joinThreadPool();
printf("IPCThreadState joinThreadPool\n");
return 0;
}
BinderTestService代码如下:
#include<binder/IServiceManager.h>
#include<binder/IPCThreadState.h>
#include<utils/Log.h>
#include"BinderTestService.h"
namespace android{
//这个函数是将自己注册进servicemanager
void BinderTestService::instantiate(){
int ret = 0;
ret = defaultServiceManager()->addService(String16("android.test.IBinderTest"),new BinderTestService());
printf("addService ret=%d\n",ret);
}
//这个函数是我们要实现的功能,其在BnBinderTest的onTransact中被调用
status_t BinderTestService::binderTest(const char *str)
{
printf("print string:%s\n",str);//简单的打印一个字串
return NO_ERROR;
}
BinderTestService::BinderTestService()
{
printf("BinderTestService is created\n");
}
BinderTestService::~BinderTestService()
{
printf("BinderTestService is destroyed\n");
}
status_t BinderTestService::onTransact(uint32_t code,const Parcel &data,Parcel *reply,uint32_t flags)
{
printf("BinderTestService onTransact\n");
return BnBinderTest::onTransact(code,data,reply,flags);//调用父类的函数
}
};
BnBinderTest代码如下:
#include <binder/Parcel.h>
#include "BnBinderTest.h"
namespace android {
//对BnBinderTest的onTransact实现,还会在后面其子类BnBinderTestService中重写此方法
status_t BnBinderTest::onTransact(uint32_t code,const Parcel &data,Parcel *reply,uint32_t flags)
{
switch(code){
case HW_HELLOWORLD2:{//在IBinderTest.h接口中定义
CHECK_INTERFACE(IBinderTest,data,reply);//检查接口
const char *str;
str = data.readCString();
reply->writeInt32(binderTest(str));//这里调用执行我们定义的binderTest函数
return NO_ERROR;
}break;
default:
return BBinder::onTransact(code,data,reply,flags);
}
}
};
大概的代码调用流程是这样的:
首先创建ProcessState和ServiceManger,然后启动BinderTestService的instantiate,这里面最关键的一步,就是defaultServiceManager()->addService(String16("android.test.IBinderTest"),new BinderTestService());将BinderTestService注册到ServiceManger中,起了个名叫android.testIBinderTest来标记一下。之后启动ProcessState进行,然后将IPCThreadState添加到线程池中。服务端的代码启动完成了。
客户端代码,main_client代码如下:
#define LOG_TAG "main_helloworldclient"
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include "../common/IBinderTest.h"
using namespace android;
int main(void/*int argc,char *argv[]*/)
{
printf("HelloWorldSevice client is starting now");
//获取ServiceManager,从而能得到远程IBinder,实现通信
sp<IServiceManager> sm =defaultServiceManager();
sp<IBinder> b;
sp<IBinderTest> sBinderTest;
do{
b=sm->getService(String16("android.test.IBinderTest"));
if(b != 0){
break;
}
printf("BinderTest is not working,waiting...\n");
usleep(500000);
}while(true);
sBinderTest = interface_cast<IBinderTest>(b);
sBinderTest->binderTest("Hello,World!\n");
return 0;
}
通过b=sm->getService(String16("android.test.IBinderTest"));获取了刚才在serviceManger中注册的BinderTestService。然后调用sBinderTest->binderTest("Hello,World!\n");传参Hello,World。
实际上binderTest的实现如下:
status_t BpBinderTest::binderTest(const char *str)
{
Parcel data,reply;
data.writeInterfaceToken(IBinderTest::getInterfaceDescriptor());
data.writeCString(str);
//通过code调用远程BinderTest方法,传递data
status_t status = remote()->transact(HW_HELLOWORLD2,data,&reply);
if(status != NO_ERROR){
printf("BinderTest error: %s",strerror(-status));
}else{
status = reply.readInt32();
}
return status;
}
这里面就用到了binder机制中的东西了,通过这个transact,将参数传递给service。
而在Server中,通过onTransact来处理传递过来的事件。
status_t BnBinderTest::onTransact(uint32_t code,const Parcel &data,Parcel *reply,uint32_t flags)
{
switch(code){
case HW_HELLOWORLD2:{//在IBinderTest.h接口中定义
CHECK_INTERFACE(IBinderTest,data,reply);//检查接口
const char *str;
str = data.readCString();
reply->writeInt32(binderTest(str));//这里调用执行我们定义的binderTest函数
return NO_ERROR;
}break;
default:
return BBinder::onTransact(code,data,reply,flags);
}
}
};
简单说来,就是client调用transact发送请求,service通过onTransact接收到请求。刨除中间的实现来看,就像是Client端直接调用了binderTest方法似的。
运行结果如下: 先运行main_server,后台运行。然后执行main_client,通过binder,调用到了BinderTestService的binderTest方法,打印输出了Hello,World。
之前遇到的卡住的问题是这样,当直接执行的时候,现场如下:
addService时,直接返回-1。调查的过程比较曲折,结果却很简单,原因是由于main_server的权限不够,没有权限使用binder设备,所以返回了-1。这里用了临时性的对策,使用命令su将用户切换成超级用户,然后再执行可执行程序即可。
此内容由EEWORLD论坛网友Bingqi23原创,如需转载或用于商业用途需征得作者同意并注明出处