Mojo Services
一个 Service 通过提供一个或多个 Mojo 接口来暴露一套服务,一个服务可以通过 Connector 来调用其他的服务,但并不是所有的服务之间都可以随意调用,而是通过 Service Manager 来管理多个 Service 间的依赖关系,只有明确表示有依赖关系的服务才能够被调用,而依赖关系则是通过 Manifest 来定义的,所有的这些 Manifest 组合在一起称为一个 Catalog。 这套机制的实现使用 Mojom 定义接口,其中最重要的是 service_manager, service, connector。
下图展示了 Service A 和 Service B 通过定义 Manifest 依赖,使用 Connector 相互调用的示意图:
当 Service A 要调用 Serivce B 提供的接口的时候,通过自己的 Connector 对象向 ServiceManager 发起接口 Binding 请求,Service Manager 会收到这个请求,然后根据已经注册的 Manifest 依赖关系,验证 Service A 是否声明了对 Service B 的依赖,如果是,则 Service Manager 会检查 Service B 是否已经存在,如果已经存在,则使用 Serivce B,否则启动一个新的 Service B 实例,这个实例可能运行在一个独立的进程中,这样 Service A 就获得了由 Service B 提供的一个 Mojo 接口。
需要注意的是,Services 是多进程架构,以上的 Service A,Service B 以及 Service Mananger 都可以运行在独立的进程中。Service Manager 所在的进程称为 Borker 进程。为了方便用户使用该多进程架构,Services 模块提供了一套创建新的可执行程序的框架,目前 Chromium 的启动就使用了该框架,该框架内部会进行一些常用功能的初始化,比如日志,崩溃时的 handler,CommandLine,mojo,Tracker/Trace,LANG 等,其中最重要的是 process_type,默认情况下进程是 Embedder 类型的,在 Chromium 中只使用了这个类型。详细信息可以查看 services/service_manager/embedder/main.cc。
service_manager::Main()
service_manager::Main() 可以帮助我们初始化很多有用的模块,使用方法如下:
#include "base/at_exit.h"
// For services
#include "services/service_manager/embedder/main.h"
#include "services/service_manager/embedder/main_delegate.h"
// 需要先实现一个MainDelegate
class DemoServerManagerMainDelegate : public service_manager::MainDelegate {
public:
DemoServerManagerMainDelegate() {}
// 默认情况下ServiceManager最终会调到这里,将控制权交给用户自己的代码
int RunEmbedderProcess() override {
// 自定义进程的行为
...
}
};
int main(int argc, const char** argv) {
// process_type 为 Service 的进程不会初始化这个
// 应该是Chromium的bug,因为Chromium中没有使用Service类型的进程
base::AtExitManager at_exit;
DemoServerManagerMainDelegate delegate;
service_manager::MainParams main_params(&delegate);
main_params.argc = argc;
main_params.argv = argv;
return service_manager::Main(main_params);
}
注意: 要是用 Services,并不是一定非要使用以上的启动逻辑,只是以上启动逻辑会帮忙初始化很多有用的模块,用起来比较简单罢了。
创建一个 Service
Service 一般都要提供一些能力 Capacity 给其他 Service 使用,这些 Capacity 是以 Mojo 接口的形式提供的,因此,要创建 Service,需要先定义 Mojo 接口:
定义一个 TestInterface 接口:
module demo.mojom;
interface TestInterface {
Hello(string who);
};
实现该接口:
// 在新版本中这些类被重命名,这里模拟新版本
template <class T>
using Remote = mojo::InterfacePtr<T>;
template <class T>
using PendingRemote = mojo::InterfacePtrInfo<T>;
template <class T>
using Receiver = mojo::Binding<T>;
template <class T>
using PendingReceiver = mojo::InterfaceRequest<T>;
class TestInterfaceImpl : public demo::mojom::TestInterface {
public:
TestInterfaceImpl(PendingReceiver<TestInterface> receiver)
: receiver_(this, std::move(receiver)) {}
void Hello(const std::string& who) {
LOG(INFO) << "TestInterfaceImpl run: Hello " << who;
}
private:
Receiver<demo::mojom::TestInterface> receiver_;
};
实现一个 Service:
// 这里故意把Service的实现和接口的实现分开,方便演示
class TestService : public service_manager::Service {
public:
void OnStart() override {
LOG(INFO) << "TestService Start.";
// 演示从一个服务内请求其它服务提供的接口
Remote<RootInterface> root;
context()->connector()->BindInterface("consumer_service",
mojo::MakeRequest(&root));
root->Hi("TestService");
}
// 当其他服务调用connector->Connect()时会触发这里
void OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override {
LOG(INFO) << "OnBindInterface: " << interface_name;
// 这里可以使用BinderRegistry来简化
if (interface_name == TestInterface::Name_) {
test_interface_.reset(new TestInterfaceImpl(
PendingReceiver<TestInterface>(std::move(interface_pipe))));
}
}
private:
std::unique_ptr<TestInterfaceImpl> test_interface_;
};
Servcie 要定义自己的 Manifest 来描述自己提供的能力以及以来的其他 Servcies,Manifest 由一个json 文件描述:
// mojom/test_servcie_manifest.json
{
"name": "test_service",
"display_name": "Test Service",
"interface_provider_specs": {
"service_manager:connector": {
"provides": {
"test": ["demo.mojom.TestInterface"]
},
"requires": {
"consumer_service":["root"]
}
}
}
}
这里可以看到,我们的服务名字为 test_servcie,它通过 demo.mojom.TestInterface 提供了 test 能力,并且依赖 consumer_service 服务的 root 能力。
接下来需要在 BUILD.gn 文件中,将该 json 定义的 Manifest 生成代码可访问的 Catalog,在 BUILD.gn 中添加以下内容:
import("//services/catalog/public/tools/catalog.gni")
import("//services/service_manager/public/cpp/service.gni")
import("//services/service_manager/public/service_manifest.gni")
service_manifest("test_service_manifest") {
name = "test_service"
source = "mojom/test_service_manifest.json"
}
service_manifest("consumer_service_manifest") {
name = "consumer_service"
source = "mojom/consumer_service_manifest.json"
}
catalog("test_service_catalog") {
embedded_services = [
":test_service_manifest",
":consumer_service_manifest"
]
}
# 这会生成一个名为 demo::services::CreateTestServiceCatalog() 的全局方法
catalog_cpp_source("test_service_catalog_source") {
catalog = ":test_service_catalog"
generated_function_name = "demo::services::CreateTestServiceCatalog"
}
现在万事具备,可以创建 ServiceManager 了,注意这里调用了自动生成的 demo::services::CreateTestServiceCatalog() 方法,相当于在 ServiceManager 中注册了这种服务:
service_manager::BackgroundServiceManager service_manager(
&service_process_launcher_delegate,
demo::services::CreateTestServiceCatalog());
有了 ServiceManager 就可以启动服务了:
// 可以使用以下方式手动启动一个Service
service_manager.StartService(service_manager::Identity("test_service"));
这样启动后,service_manager 检测到 test_service 不存在,所以就在新进程中启动一个 test_service 服务,在新进程中 test_service 的 OnStart 方法就会被调用,test_service 在 OnStart 方法中就可以使用 Connector 去请求 consumer_service 提供的 root 能力了。
class TestService : public service_manager::Service {
...
void OnStart() override {
LOG(INFO) << "TestService Start.";
// 演示从一个服务内请求其它服务提供的接口
Remote<RootInterface> root;
context()->connector()->BindInterface("consumer_service",mojo::MakeRequest(&root));
root->Hi("TestService");
}
...
};
至此,Service 的整条流程也走完了,这里省略了一些逻辑,还有一些其他用法也不赘述了,,具体请查看 demo_services.cc。
Services 的代码位于 Chromium 中的 //services 目录下,需要说明的是,services 并不是完全独立的,它是和 Chromium 紧密相关的,内部存放了大量的 Chromium 相关的 service,比如 device,audio 等,因此如果想像 Mojo 那样在其他和 Chromium 无关的项目中使用可能会比较麻烦。