Binder进程间通信机制详解

本文详细解析了Android系统的Binder通信机制,包括其优势、角色介绍、Binder驱动、引用计数、服务管理等内容,并通过实例展示了Binder通信过程。通过对Binder的深入理解,有助于开发者更好地掌握Android进程间通信。
摘要由CSDN通过智能技术生成

overview

Binder是Android系统进程间通信方式之一。

相比传统的IPC通信,binder的优点是:

  1. 只需要一次内存拷贝
  2. 安全可靠(在内核中添加UID等)
  3. Android系统中广泛使用Client-Server的通信方式,Binder提供了这一机制。

binder通信中的四个角色:Client,Server,ServiceManager,binder驱动。

 

Binder驱动:提供进程间通信方式的机制,上图中所有跨进程的通信都需要通过Binder驱动

ServiceManager:服务管理者,所有的服务都需要首先注册到ServiceManager中才可以被其他进程访问到,作为一个特殊的系统服务,在系统启动时启动服务。

Server:服务的提供者,首先需要将服务注册到ServiceManger中,后续处理Client的请求。

Client:服务的使用者,首先需要向ServiceManager请求服务句柄,后续通过这个句柄请求服务。

 

编写一个Binder通信实例

功能:Server提供一个变量的访问接口,Client需要通过这个接口实现跨进程的访问。

virtual int32_t getVal() = 0;
virtual void setVal(int32_t val) = 0;

为了方便Binder的使用,系统为我们提供一套接口,我们只需要实现对应的接口即可使用Binder通信。这个接口使用了代理模式,如下所示。

BnInterface和BpInterface都继承了模板接口类INTERFACE,这个模板接口类就是Service提供的接口。这里Bn表示本地服务接口,Bp表示远程代理接口。

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
    virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor); //Bn端重写这一接口,根据descriptor返回一个IInterface接口,Bp端使用默认实现,返回NULL
    virtual const String16&     getInterfaceDescriptor() const;

protected:
    typedef INTERFACE           BaseInterface;
    virtual IBinder*            onAsBinder();
};

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
    explicit                    BpInterface(const sp<IBinder>& remote);

protected:
    typedef INTERFACE           BaseInterface;
    virtual IBinder*            onAsBinder();
};

INTERFACE需要继承Binder通信库提供的接口IInterface,所以我们的接口定义如下

class IFregService: public IInterface
{
    public:
        DECLARE_META_INTERFACE(FregService); //通过宏定义声明的默认接口
        virtual int32_t getVal() = 0;
        virtual void setVal(int32_t val) = 0;
};

Bn端的Service需要实现BnInterface接口,Bp端的Client需要实现BpInterface接口。

Bn端的实现如下所示:

class BnFregService: public BnInterface<IFregService> //继承实现BnInterface接口
{
    public:
        virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};

IMPLEMENT_META_INTERFACE(FregService, "shy.luo.IFregService");

status_t BnFregService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code)
    {
        case GET_VAL:
            {
                CHECK_INTERFACE(IFregService, data, reply);
                int32_t val = getVal();
                reply->writeInt32(val);

                return NO_ERROR;
            }
        case SET_VAL:
            {
                CHECK_INTERFACE(IFregService, data, reply);
                int32_t val = data.readInt32();
                setVal(val);

                return NO_ERROR;
            }
        default:
            {
                return BBinder::onTransact(code, data, reply, flags);
            }
    }
}

onTransact()是一个继承自BBinder的接口,进程收到服务请求后,最终会通过这个接口处理。其中:

code:输入,表示双方约定的协议代码,GET_VAL表示请求服务端的get_val()接口,SET_VAL表示请求服务端的set_val()接口。

data:输入,保存了接口需要的参数等。

reply:输出,接口的输出值保存在这个类中。

flags:暂时没有用

onTransact会根据收到的服务请求调用本地不同的接口,并将返回值填入reply中,之后binder库会完成与驱动通信的所有事件。

CHECK_INTERFACE会判断请求是不是对应的代理对象发送过来的(通过代理对象的写的数据头),如果不是,就不会继续往下执行。

这里的BnFregService仍然是一个纯虚类,需要继续实现服务接口:

class FregServer : public BnFregService
{
    public:
        static void instantiate()
        {
            defaultServiceManager()->addService(String16(FREG_SERVICE), new FregServer());
        }

        int32_t getVal()
        {
            return m_val;
        }

        void setVal(int32_t val)
        {
            m_val = val;
        }                                                                                                                              
    private:
        int32_t m_val = 1;
};

Bn端提供了访问类成员m_val的接口,Bp端的调用最终访问的就是这个接口。

并且Bn端提供了一个注册服务的静态方法,将实例化的本地类FregServer通过服务名FREG_SERVICE注册到service manager中。

服务端的main函数实现:

int main(int argc, char** argv)
{
    FregServer::instantiate();
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

    return 0;
}

首先将FregServer类实例化,并注册到service Manager中,然后启动binder线程池开始监听client发送过来的请求,所有的这些请求最终都会被转发到onTransact()方法中处理。

Bp端的实现如下所示:

class BpFregService: public BpInterface<IFregService>
{
    public:
        BpFregService(const sp<IBinder>& impl)
            : BpInterface<IFregService>(impl)
        {
                                                                                                                                               }
    public:
        int32_t getVal()
        {
            Parcel data;
            data.writeInterfaceToken(IFregService::getInterfaceDescriptor());

            Parcel reply;
            remote()->transact(GET_VAL, data, &reply);

            int32_t val = reply.readInt32();

            return val;
        }

        void setVal(int32_t val)
        {
            Parcel data;
            data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
            data.writeInt32(val);                                                                                                      
            Parcel reply;
            remote()->transact(SET_VAL, data, &reply);
        }                                                                                                                                     };

Bp端重写了我们定义的接口,以getVal()为例,

writeInterfaceToken将通信头写入到输入数据data中,remote()返回的是一个BpBinder,所以这里调用了BpBinder的transact方法,将通信请求发送到服务端,再从返回结果reply中读出服务端的返回数据。

这个跨进程通信的过程通过封装,对于用户来说是通过代理接口就可以直接返回本地接口的处理结果。

Client端的main函数:

int main()
{
    sp<IBinder> binder = defaultServiceManager()->getService(String16(FREG_SERVICE));
    if(binder == NULL) {
        ALOGE("Failed to get freg service: %s.\n", FREG_SERVICE);
        return -1;
    }

    sp<IFregService> service = IFregService::asInterface(binder);
    if(service == NULL) {
        ALOGE("Failed to get freg service interface.\n");
        return -1;
    }
    printf("Read original value from FregService:\n");

    int32_t val = service->getVal();
    printf(" %d.\n", val);

    printf("Add value 1 to FregService.\n");

    val += 1;
    service->setVal(val);

    printf("Read the value from FregService again:\n");

    val = service->getVal();
    printf(" %d.\n", val);

    return 0;
}

首先通过服务名,使用ServiceManager的getService(

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值