当前位置: 首页 > 科技观察

源码分析——Proxy-Stub架构之谜剖析

时间:2023-03-18 15:22:42 科技观察

更多内容请访问:与华为官方共建的鸿蒙技术社区https://ost.51cto.com服务概览openHarmony中有很多服务。一般来说,一个应用程序是可以调用B服务的方法的,就像在自己的进程中调用一样。这里的具体实现其实是通过binder驱动来实现的。Binder驱动通过mmap将内核态代码映射到用户态,直接读写数据,从而完成跨进程调用。但这不是本文的重点。本片主要讲proxy-stub的设计模式。服务的一般编码方式采用proxy-stub架构编程,大致分为以下三个步骤:设计接口类继承IRemoteBroker,接口方法一般设计为虚方法。设计代理类继承自IRemoteProxy,实现sendRequest方法和自己的虚方法。设计存根类继承自IRemoteStub,实现OnRemote方法和自己的虚方法。这样我们在调用的时候就可以像调用存根类的接口方法一样调用代理类的接口方法了。源码分析我们通过阅读源码来揭开它的神秘面纱。我们现在关注几个关键类。IRemoteObject:classIRemoteObject:publicvirtualParcelable,publicvirtualRefBase{public:enum{IF_PROT_DEFAULT,/*调用者家族。*/IF_PROT_BINDER=IF_PROT_DEFAULT,IF_PROT_DATABUS,};枚举{DATABUS_TYPE,};classDeathRecipient:publicRefBase{public:enum{ADD_DEATH_RECIPIENT,REMOVE_DEATH_RECIPIENT,NOTICE_DEATH_RECIPIENT,TEST_SERVICE_DEATH_RECIPIENT,TEST_DEVICE_DEATH_RECIPIENT,};virtualvoidOnRemoteDied(constwptr&object)=0;};虚拟int32_tGetObjectRefCount()=0;virtualintSendRequest(uint32_tcode,MessageParcel&data,MessageParcel&reply,MessageOption&option)=0;虚拟布尔IsProxyObject()常量;虚拟布尔CheckObjectLegality()常量;virtualboolAddDeathRecipient(constsptr&recipient)=0;虚拟的boolRemoveDeathRecipient(constsptr&recipient)=0;virtualboolMarshalling(Parcel&parcel)常量覆盖;staticIRemoteObject*Unmarshalling(Parcel&parcel);staticboolMarshalling(Parcel&parcel,constsptr&objectsp;IRemoteBroker>AsInterface();virtualintDump(intfd,conststd::vector&args)=0;conststd::u16string描述符_;std::u16stringGetObjectDescriptor()const;protected:explicitIRemoteObject(std::u16stringdescriptor=nullptr);};这个是binder驱动中真正的数据传输类,继承自Parcelable,继承自RefBase可以理解为一个智能指针的控制块,在openharmony中,这里并没有直接使用C++标准库中的智能指针,而是利用sptr和refbase一起构建,即原始指针和控制块相关信息,使用后一种方法更加解耦,符合复杂架构的设计理念。IRemoteBroker:classIRemoteBroker:publicvirtualRefBase{public:IRemoteBroker()=default;虚拟~IRemoteBroker()override=default;虚拟sptrAsObject()=0;静态内联sptrAsImplement(constsptr&object){returnnullptr;}};#defineDECLARE_INTERFACE_DESCRIPTOR(DESCRIPTOR)\staticinlineconststd::u16stringmetaDescriptor_={DESCRIPTOR};\staticinlineconststd::u16string&GetDescriptor()\{\returnmetaDescriptor_;\}一般的接口类,通过metaDescriptor_用作表示区分类知识。IRemoteProxy:namespaceOHOS{templateclassIRemoteProxy:publicPeerHolder,publicINTERFACE{public:explicitIRemoteProxy(constsptr&object);~IRemoteProxy()override=default;受保护:sptrAsideObject();};templateIRemoteProxy::IRemoteProxy(constsptr&object):PeerHolder(object){}templatesptrIRemoteProxy::AsObject(){returnRemote();}}//namespaceOHOSIRemoteProxy使用c++的crtp(奇怪的循环模板模式)编程,使得父类可以调用子类的方法。继承自peerhold(实际上,它包含一个IRemoteObject对象)。IRemoteStub:命名空间OHOS{模板类IRemoteStub:公共IPCObjectStub,公共接口{public:IRemoteStub();虚拟~IRemoteStub()=默认值;sptrAsObject()覆盖;sptrAsideface()覆盖;};templateIRemoteStub::IRemoteStub():IPCObjectStub(INTERFACE::GetDescriptor()){}templatesptrIRemoteStub::AsInterface(){returnthis;}templatesptrIRemoteStub::AsObject(){returnthis;}}//命名空间OHOSstub对象比代理对象更复杂,它也用crtp编程。会继承IPCObjectStub(也是iremoteObject对象)。看到这里,可能有人会疑惑为什么proxy调用直接调用到stub端呢?其实奥妙就在于stub继承的IPCObjectStub(继承自iremoteObject)对象,也就是iremoteObject对象。proxy的构造继承peerhold,peerhold类中的iremoteObject对象和IPCObjectStub是什么关系?其实peerhold是IPCObjectStub的引用对象,实际类型是IPCObjectProxy。这两个在ipc框架中,IPCObjectProxy实际使用的是sendRequest,而IPCObjectStub会调用OnremoteRequest。如果大家有兴趣,我们下次再分析一下IPC框架是如何实现的。更多信息请访问:与华为官方共建的鸿蒙技术社区https://ost.51cto.com