[原创] 【Android 开发学习之路】三 -- Binder初实现

Bingqi23   2020-3-30 19:57 楼主

    时间过去了挺久了,直到现在才更新,是遇到了两个问题,一是代码是实际运行的时候报错,排错用了挺长时间,再一个就是一直没有倒出来时间写。
    言归正传,现在进行到了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的原理之后再单独开贴描述,现在还是只重实践。

首先,代码结构如下:

2020-04-06_095823.png 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(/*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方法似的。

运行结果如下: 1.png 先运行main_server,后台运行。然后执行main_client,通过binder,调用到了BinderTestService的binderTest方法,打印输出了Hello,World。

 

之前遇到的卡住的问题是这样,当直接执行的时候,现场如下:

2.png addService时,直接返回-1。调查的过程比较曲折,结果却很简单,原因是由于main_server的权限不够,没有权限使用binder设备,所以返回了-1。这里用了临时性的对策,使用命令su将用户切换成超级用户,然后再执行可执行程序即可。

 

 


此内容由EEWORLD论坛网友Bingqi23原创,如需转载或用于商业用途需征得作者同意并注明出处

本帖最后由 Bingqi23 于 2020-4-6 11:22 编辑

回复评论 (1)

嗯。。。没找到上传附件的地方。需要原代码包的私信吧

点赞  2020-4-6 11:24
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复