0. 前置逻辑
incoming_receiver_通过InterfaceEndpointClient::InterfaceEndpointClient创建时设置。
在多个地方可以新建InterfaceEndpointClient。
InterfacePtrStateBase::InitializeEndpointClient、BindingStateBase::BindInternal、AssociatedBindingBase::BindImpl。
分别在这三条线上被调用:
[1] mojo::InterfacePtrStateBase::ConfigureProxyIfNecessary --> mojo::InterfacePtrStateBase::InitializeEndpointClient 。
[2] mojo::internal::BindingState<...>::Bind --> mojo::internal::BindingStateBase::BindInternal 。
[3] mojo::AssociatedBinding::Bind --> mojo::AssociatedBindingBase::BindImpl 。
本文将分别介绍这三条线。
A InitializeEndpointClient分支,这通常是Browser中某个功能需要进行连接以执行一些时会调用到它。
[1.0]. 前置的具体逻辑
稍后举例。
[1.1]. mojo::InterfacePtr<chrome::mojom::XXXX>::*
这个步骤不是必须的。如果你观察interface_ptr_state.h文件,你会发现里面几乎所有操作都会调用ConfigureProxyIfNecessary。这个操作也不例外,通常在调用具体功能前,代码会调用到InterfacePtr中的操作函数,并继续后面的流程。
[1.2]. mojo::internal::InterfacePtrState<chrome::mojom::XXXX>::*
这里XXXX和上面一层的是一样的,代表具体的mojom消息的实现接口类。函数名也会和上一层的一样。因为这里只是具体的操作实现辅助类。
[1.3]. mojo::internal::InterfacePtrState<chrome::mojom::XXXX>::ConfigureProxyIfNecessary
在上一步的操作中通常都会走到本函数。这个函数会调用InitializeEndpointClient创建InterfaceEndpointClient对象,并将自己模板类中的mojom接口类XXXX作为其实现,以注册端点客户端。
[1.4]. mojo::internal::InterfacePtrStateBase::InitializeEndpointClient
我们这次的目标函数。通过endpoint_client_.reset(new InterfaceEndpointClient(...))来建立新的InterfaceEndpointClient。
=================================================================================================
看一下[1.0].的前置逻辑,以Chrome的设备性能调查器为例。
前置有如下调用链:
…… --> performance_manager::MetricsCollector::RecordExpectedQueueingTimeForUkm -->
ukm::internal::UkmEntryBuilderBase::Record -->
ukm::MojoUkmRecorder::AddEntry
AddEntry函数接受一个mojom::UkmEntryPtr entry作为参数,并调用interface_->AddEntry(std::move(entry))来添加项目。
在MojoUkmRecorder::MojoUkmRecorder构造函数时,interface_即会被初始化为mojom::UkmRecorderInterfacePtr。
当调用UkmRecorderInterfacePtr::AddEntry 时,实际上需要从ProxyClass走,也即根据生成的代码,调用:UkmRecorderInterfaceProxy::AddEntry(UkmEntryPtr in_entry),并转入receiver_->Accept。
receiver_在UkmRecorderInterfaceProxy::UkmRecorderInterfaceProxy(mojo::MessageReceiverWithResponder* receiver)构造时即被传入。
mojo::MessageReceiverWithResponder::Accept会调用ForwardMessageCallback,forward_,将消息转发。
ForwardMessageCallback是base::Callback<void(Message)>的语法糖。随后base::Callback又是RepeatingCallback<Signature>的语法糖。最终,回调会返回到InterfacePtr<chrome::mojom::UkmRecorderInterface>::Accept
,然后它调用InterfacePtr<chrome::mojom::UkmRecorderInterface>::ForwardMessage函数,并在调用的第一时间调用[1.1]。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
B BindInternal 分支
通常用于代码需要将自己和后端处理代码进行绑定时被调用到,具体参考示例。
[2.0]. 前置的具体逻辑
稍后举例。
[2.1]. mojo::Binding<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::Bind
这个函数会直接调用BindState::Bind。
[2.2]. mojo::internal::BindingState<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::Bind
这个函数会直接调用BindInternal。
[2.3]. mojo::internal::BindingStateBase::BindInternal
目标函数,通过endpoint_client_.reset(new InterfaceEndpointClient(...))新建一个InterfaceEndpointClient对象。
以这个函数network::`anonymous namespace'::SimpleURLLoaderImpl::StartRequest的调用为例:
mojom::URLLoaderClientPtr client_ptr; client_binding_.Bind(mojo::MakeRequest(&client_ptr)); client_binding_.set_connection_error_handler(base::BindOnce(&SimpleURLLoaderImpl::OnConnectionError, base::Unretained(this)));
mojo::Binding<mojom::URLLoaderClient>,client_binding_是URLLoaderClient的消息管道(message pipe)。通过将URLLoaderClientPtr绑定到管道上,这样后续就可以通过这个管道进行通信了。client_binding_在SimpleURLLoaderImpl构造时即会被初始化为this。
根据https://www.chromium.org/developers/design-documents/mojo/associated-interfaces 的说法,Binding,与C++绑定API类似,我们有:
*`mojo.interfacePtrInfo`和`mojo.interfaceRequest`封装消息管道的两端。它们分别表示接口连接的客户端和服务端。
*对于每个Mojom接口`Foo`,都有一个生成的`FooPtr`类。它拥有一个`InterfacePtrInfo`;提供了一种:通过使用`InterfacePtrInfo`的消息管道句柄,来发送接口调用的方法。
*`mojo.binding`拥有`InterfaceRequest`,它侦听消息管道句柄,并将传入的消息分派给用户定义的接口实现。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
C BindImpl 分支
[3.0]. 前置的具体逻辑
稍后举例。
[3.1]. mojo::AssociatedBinding<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::AssociatedBinding
[3.2]. mojo::AssociatedBinding<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::Bind
[3.3]. mojo::AssociatedBindingBase::BindImpl
这个分支和B分支很像,上三层的结构都非常简单。直接调用到BindImpl后,通过endpoint_client_.reset(new InterfaceEndpointClient(...))新建一个InterfaceEndpointClient对象。
根据https://www.chromium.org/developers/design-documents/mojo/associated-interfaces 的说法,
associated interface连接没有自己的基础消息管道。它必须与现有的消息管道(即interface connection)相关联。
与非关联的接口连接相似的是,
`mojo.AssociatedInterfacePtrInfo`和`mojo.AssociatedInterfaceRequest`封装route ID,表示消息管道上的逻辑连接。
对于每个Mojom接口`Foo`,都有一个生成的`FooAssociatedPtr`类。它拥有一个`AssociatedInterfacePtrInfo`,即接口的客户端。
`mojo.AssociatedBinding`拥有`AssociatedInterfaceRequest`,它侦听连接并将传入消息分派给用户定义的接口实现。