服务概览
openHarmony 中存在很多的服务,一般来说可以使得A应用调用B服务的方法,就像在自己进程中调用一样,这里具体的实现实际通过binder驱动实现。binder驱动通过mmap将内核态代码映射到用户态,直接读写数据这样就完成了跨进程的调用。不过这不是该篇内容的重点,本片主要讲一下proxy - stub 的设计模式。
服务的一般编码模式
使用proxy - stub 架构编程,大致可以分为以下三个步骤:
- 设计接口类继承 IRemoteBroker,接口方法一般设计为虚方法。
- 设计proxy类 继承至 IRemoteProxy,并且实现sendRequest方法和自身虚方法。
- 设计stub类 继承至 IRemoteStub ,并且实现OnRemote方法和自身虚方法。
这样我们就可以在调用是调用proxy类的接口方法就像调用stub类的接口方法一样了。
源码剖析
我们通过阅读源码,解开其神秘的面纱。我们现在关注几个重点的类。
IRemoteObject:
class IRemoteObject : public virtual Parcelable, public virtual RefBase {
public:
enum {
IF_PROT_DEFAULT,
IF_PROT_BINDER = IF_PROT_DEFAULT,
IF_PROT_DATABUS,
};
enum {
DATABUS_TYPE,
};
class DeathRecipient : public RefBase {
public:
enum {
ADD_DEATH_RECIPIENT,
REMOVE_DEATH_RECIPIENT,
NOTICE_DEATH_RECIPIENT,
TEST_SERVICE_DEATH_RECIPIENT,
TEST_DEVICE_DEATH_RECIPIENT,
};
virtual void OnRemoteDied(const wptr<IRemoteObject> &object) = 0;
};
virtual int32_t GetObjectRefCount() = 0;
virtual int SendRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) = 0;
virtual bool IsProxyObject() const;
virtual bool CheckObjectLegality() const;
virtual bool AddDeathRecipient(const sptr<DeathRecipient> &recipient) = 0;
virtual bool RemoveDeathRecipient(const sptr<DeathRecipient> &recipient) = 0;
virtual bool Marshalling(Parcel &parcel) const override;
static IRemoteObject *Unmarshalling(Parcel &parcel);
static bool Marshalling(Parcel &parcel, const sptr<IRemoteObject> &object);
virtual sptr<IRemoteBroker> AsInterface();
virtual int Dump(int fd, const std::vector<std::u16string> &args) = 0;
const std::u16string descriptor_;
std::u16string GetObjectDescriptor() const;
protected:
explicit IRemoteObject(std::u16string descriptor = nullptr);
};
这就是真正在binder驱动中数据传输的类,继承自 Parcelable 。而继承RefBase 可以理解为智能指针的控制块。openharmony中这里并没有直接使用c++标准库中的智能指针,而是使用 sptr 和refbase两个类共同构建,也就是裸指针和控制块相关信息。使用后者的方式,更加解耦。符合复杂架构设计理念。
IRemoteBroker:
class IRemoteBroker : public virtual RefBase {
public:
IRemoteBroker() = default;
virtual ~IRemoteBroker() override = default;
virtual sptr<IRemoteObject> AsObject() = 0;
static inline sptr<IRemoteBroker> AsImplement(const sptr<IRemoteObject> &object)
{
return nullptr;
}
};
#define DECLARE_INTERFACE_DESCRIPTOR(DESCRIPTOR) \
static inline const std::u16string metaDescriptor_ = { DESCRIPTOR }; \
static inline const std::u16string &GetDescriptor() \
{ \
return metaDescriptor_; \
}
一般的接口类,通过metaDescriptor_ 作为表示区分标识。
IRemoteProxy:
namespace OHOS {
template <typename INTERFACE> class IRemoteProxy : public PeerHolder, public INTERFACE {
public:
explicit IRemoteProxy(const sptr<IRemoteObject> &object);
~IRemoteProxy() override = default;
protected:
sptr<IRemoteObject> AsObject() override;
};
template <typename INTERFACE>
IRemoteProxy<INTERFACE>::IRemoteProxy(const sptr<IRemoteObject> &object) : PeerHolder(object)
{
}
template <typename INTERFACE> sptr<IRemoteObject> IRemoteProxy<INTERFACE>::AsObject()
{
return Remote();
}
} // namespace OHOS
IRemoteProxy 使用c++的crtp (奇特重现模板模式)编程,使得父类可以调用子类的方法。继承自peerhold (其实就是包括一个IRemoteObject对象) 。
IRemoteStub:
namespace OHOS {
template <typename INTERFACE> class IRemoteStub : public IPCObjectStub, public INTERFACE {
public:
IRemoteStub();
virtual ~IRemoteStub() = default;
sptr<IRemoteObject> AsObject() override;
sptr<IRemoteBroker> AsInterface() override;
};
template <typename INTERFACE> IRemoteStub<INTERFACE>::IRemoteStub() : IPCObjectStub(INTERFACE::GetDescriptor()) {}
template <typename INTERFACE> sptr<IRemoteBroker> IRemoteStub<INTERFACE>::AsInterface()
{
return this;
}
template <typename INTERFACE> sptr<IRemoteObject> IRemoteStub<INTERFACE>::AsObject()
{
return this;
}
} // namespace OHOS
stub对象较于proxy对象复杂一些,也使用crtp编程。会继承IPCObjectStub (也是iremoteObject对象)。
看到这里,可能有人疑惑,为什么proxy调用,会直接调用到stub这端呢?其实奥秘就在于stub 继承的IPCObjectStub (继承iremoteObject) 对象,就是这个iremoteObject对象。proxy的构造继承 peerhold ,peerhold 类中的iremoteObject 对象和 IPCObjectStub 这个是什么关系呢?其实peerhold是IPCObjectStub 的引用对象,实际类型是 IPCObjectProxy 。这两者在ipc框架中,IPCObjectProxy 实际使用sendrequest ,IPCObjectStub便会调用OnremoteRequest。如果有兴趣,我们下次可以分析IPC框架具体是如何实现的。