本帖最后由 Bingqi23 于 2020-4-6 11:22 编辑
时间过去了挺久了,直到现在才更新,是遇到了两个问题,一是代码是实际运行的时候报错,排错用了挺长时间,再一个就是一直没有倒出来时间写。
言归正传,现在进行到了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的原理之后再单独开贴描述,现在还是只重实践。
首先,代码结构如下:
main_server代码如下:
- #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(){
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- BinderTestService::instantiate();
-
- 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{
-
- void BinderTestService::instantiate(){
- int ret = 0;
- ret = defaultServiceManager()->addService(String16("android.test.IBinderTest"),new BinderTestService());
- printf("addService ret=%d\n",ret);
- }
-
- 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 {
-
- status_t BnBinderTest::onTransact(uint32_t code,const Parcel &data,Parcel *reply,uint32_t flags)
- {
- switch(code){
- case HW_HELLOWORLD2:{
- CHECK_INTERFACE(IBinderTest,data,reply);
- const char *str;
- str = data.readCString();
- reply->writeInt32(binderTest(str));
- 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)
- {
- printf("HelloWorldSevice client is starting now");
-
- 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);
-
- 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:{
- CHECK_INTERFACE(IBinderTest,data,reply);
- const char *str;
- str = data.readCString();
- reply->writeInt32(binderTest(str));
- 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原创,如需转载或用于商业用途需征得作者同意并注明出处