天天看點

chromium mojo 快速入門

版權聲明:本文為CSDN部落客「tornmy」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:

  • https://blog.csdn.net/tornmy/article/details/82748058

 https://blog.csdn.net/mengxin00100/article/details/106325249?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

  • https://blog.csdn.net/qq_27022241/article/details/103906792

 這是對chromium文檔的翻譯:src/docs/mojo_and_services.md

消息管道:是一組端點. 每個endpoint有一個接受消息的隊列, 在一個端點上寫消息會高效地放入對端端點的消息隊列上。是以消息管道是雙工通信的。

mojom檔案:描述了接口,它們描述了類似proto files的強類型消息結構,通過binding generator可以産生對應于不同語言的檔案。

給定一個mojom interface和一條message pipe, 它的兩個端點可以打上InterfacePtr和Binding的标簽。現在它描述了一條這樣的message pipe,它發送由mojom interface描述的消息。

InterfacePtr:是發送消息的端點,一旦綁定到一個消息管道的端點,就可以馬上序列化要發送的消息,并寫入管道

InterfaceRequest:是接受消息的端點,本質上僅僅是一個持有消息管道端點的容器,本身不會做任何事情,需要傳遞到直到綁定了實作了mojom檔案接口的類,才能讀取消息。

考慮如下的mojom檔案:

module sample.mojom;

interface Logger {

Log(string message);

};

通過mojo的binding generator可以産生以下的logging.mojom.h的c++檔案:

namespace sample {

namespace mojom {

class Logger {

virtual ~Logger() {}

virtual void Log(const std::string& message) = 0;

using LoggerPtr = mojo::InterfacePtr<Logger>;

using LoggerRequest = mojo::InterfaceRequest<Logger>;

} // namespace mojom

} // namespace sample

 首先需要建立消息管道,通常調用MakeRequest建立:

#include "sample/logger.mojom.h"

sample::mojom::LoggerPtr logger;

auto request = mojo::MakeRequest(&logger);

MakeRequset的如下: 

template <typename Interface>

InterfaceRequest<Interface> MakeRequest(

InterfacePtr<Interface>* ptr,

scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr) {

MessagePipe pipe;

ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u),

std::move(runner));

return InterfaceRequest<Interface>(std::move(pipe.handle1));

}

可見建立消息管道的同時,InterfacePtr端已經綁定到了消息管道的一端,也就是此時,InterfacePtr已經可以向消息管道發送消息了,當然對端還未綁定,發送的消息會堆積在對端。 

接下來就需要綁定InterfaceRequest端,即LoggerRequest,一旦綁定,就會安排一個task去讀取、反序列化并分化所有可讀的消息給mojom檔案中接口實作類。例如接口類實作如下:

#include "base/logging.h"

#include "base/macros.h"

class LoggerImpl : public sample::mojom::Logger {

public:

// NOTE: A common pattern for interface implementations which have one

// instance per client is to take an InterfaceRequest in the constructor.

explicit LoggerImpl(sample::mojom::LoggerRequest request)

: binding_(this, std::move(request)) {}

~Logger() override {}

// sample::mojom::Logger:

void Log(const std::string& message) override {

LOG(ERROR) << "[Logger] " << message;

}

private:

mojo::Binding<sample::mojom::Logger> binding_;

DISALLOW_COPY_AND_ASSIGN(LoggerImpl);

此時我們可以使用前面構造的LoggerRequest構造一個LoggerImpl,一但構造完成,消息就會盡可能快地發送給LoggerImpl。最終就可以在實作類的一端看到Logger發送的“hello”。

總的流程可以概括如下:

當然有時候發送端可能期望有一個回複,此時mojom接口可以調整為

GetTail() => (string message);

其中,Log中的message參數和GetTail後置的message都是會被序列化進管道傳送的消息。對應的C++檔案如下:

using GetTailCallback = base::OnceCallback<void(const std::string& message)>;

virtual void GetTail(GetTailCallback callback) = 0;

和不需要回複的版本一樣,發送端調用的接口,在實作端也會對應的被調用,而其中的GetTail,發送端發送一個回調函數的參數以接受回複。

接受端的實作類如下

lines_.push_back(message);

void GetTail(GetTailCallback callback) override {

std::move(callback).Run(lines_.back());

std::vector<std::string> lines_;

那麼,在發送端發送過消息後,即調用過Log後,繼續如下調用GetTail

void OnGetTail(const std::string& message) {

LOG(ERROR) << "Tail was: " << message;

logger->GetTail(base::BindOnce(&OnGetTail));

那麼在實作端調用了GetTail後,将會将line_.back()序列化并發送回發送端,同時遵循發送端的内部邏輯,觸發回調函數得到發送過去的最後一條消息。 

當然在browser和renderer通信時, browser其實通過消息管道連接配接到Service Manager,由它統一管理和render程序的連接配接。此時需要在browser的manifet檔案和renderer的manifest檔案中申明,如下

content_renderer_manifest.json:

...

"interface_provider_specs": {

"service_manager:connector": {

"provides": {

"cool_ping_feature": [

"sample::mojom::Logger"

]

},

},

content_browser_manifest.json:

"interface_provider_specs": {

"requires": {

"content_renderer": [ "cool_ping_feature" ],

},

在renderer端則可以如下實作接口類,并通常在Init時注冊到Service Manager,

class LoggerImpl : mojom::Logger {

void BindToInterface(example::mojom::LoggerRequest request) {

binding_.reset(

new mojo::Binding<mojom::MemlogClient>(this, std::move(request)));

void GetTail(LoggerCallback callback) { std::move(callback).Run(4); }

std::unique_ptr<mojo::Binding<mojom::Logger>> binding_;

RenderThreadImpl::Init() {

this->logger = std::make_unique<LoggerImpl>();

auto registry = base::MakeUnique<service_manager::BinderRegistry>();

// This makes the assumption that |this->logger| will outlive |registry|.

registry->AddInterface(base::Bind(&LoggerImpl::BindToInterface), base::Unretained(this->Logger.get()));

GetServiceManagerConnection()->AddConnectionFilter(

base::MakeUnique<SimpleConnectionFilter>(std::move(registry)));

那麼此時,browser端可以如下建立與renderer的連接配接,通常有如下兩種方式

sample::mojom::LoggerPtr logger; // Make sure to keep this alive! Otherwise the response will never be received.

sample::mojom::LoggerRequest request = mojo::MakeRequest(&logger);

content::RenderProcessHost* host = GetRenderProcessHost();

content::BindInterface(host, std::move(request));

其中content::BindInterface是一個輔助接口,通過Service Manager通過postTask将request傳送給renderer,建立對應的LoggerImpl,完成連接配接。

或者

render_frame->GetRemoteInterfaces()->GetInterface(mojo::MakeRequest(&logger));

RenderProcessHost作為sender,RenderProcess為receiver的例子如下, 

void RenderProcessHostImpl::InitializeChannelProxy() {

...

content::BindInterface(this, &child_control_interface_);

template <typename Host, typename Interface>

void BindInterface(Host* host, mojo::InterfacePtr<Interface>* ptr) {

mojo::MessagePipe pipe;

ptr->Bind(mojo::InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u));

host->BindInterface(Interface::Name_, std::move(pipe.handle1));

renderer和browser建立通信,更詳細的可以看這個文章。

Mojo in Chromium

Mojo C++ System API

Mojo C++ Bindings API

Understanding mojo behavior

————————————————