CEF3开发者系列之单进程模式应用
本文基于cef_binary_3.2623.1401.gb90a3be_windows32 ,即Chromium 49。主要利用CEF3在Windows动态链接库dll中做一些辅助性界面开发。
需要解决的问题:
1、 使用单进程。由于项目是给第三方程序调用的SDK,所以不能使用多进程模式,否则增加对接成本和进程控制成本。
2、 JS与Native互调。某些界面中有前端JS与客户端Native互相调用的接口。
在CEF3中使用单进程比较简单,在初始化CEF3的时候,通过CefSettings配置进程模式。
CefSettings settings; settings.single_process = true; //采用单进程模式 settings.single_process = false; //采用多进程模式
先来简单的带一笔CEF3的进程模式介绍:
CEF3是多进程架构的。Browser被定义为主进程,负责窗口管理,界面绘制和网络交互。Blink的渲染和Js的执行被放在一个独立的Render 进程中;除此之外,Render进程还负责Js Binding和对Dom节点的访问。 默认的进程模型中,会为每个标签页创建一个新的Render进程。其他进程按需创建,例如管理插件的进程以及处理合成加速的进程等都是按需创建。
默认情况下,主应用程序会被多次启动运行各自独立的进程。这是通过传递不同的命令行参数给CefExecuteProcess函数做到的。
int exit_code = CefExecuteProcess(main_args, NULL, sandbox_info); if (exit_code >= 0) { // The sub-process has completed so return here. return exit_code; }
Browser和Render进程可以通过发送异步消息进行双向通信。甚至在Render进程可以注册在Browser进程响应的异步JavaScript API。
single_process 设置为true时,Browser和Renderer使用一个进程。此项也可以通过命令行参数“single-process”配置。
回到我们需要解决的问题:单进程模式的时候,Browser和Renderer使用一个进程,不仅满足界面的绘制,同时满足JS的执行,与Native进行互相调用。正好满足的我们的需求,幸好我们暂时不需要加载插件。
解决了方案和技术原理性问题,剩下的基本上就是编写代码了。
基于CEF3框架实现一个页面加载,需要在初始化中实现CefBrowserProcessHandler和CefRenderProcessHandler类以及浏览器显示、加载、生命周期等回调类。
初始化如下:
// Structure for passing command-line arguments. // The definition of this structure is platform-specific. CefMainArgs main_args(argc, argv); // Optional implementation of the CefApp interface. CefRefPtr<MyClentApp> app(new MyClentApp); // Execute the sub-process logic, if any. This will either return immediately for the browser // process or block until the sub-process should exit. int exit_code = CefExecuteProcess(main_args, app.get()); if (exit_code >= 0) { // The sub-process terminated, exit now. return exit_code; } scoped_ptr<MainContextImpl> mainContext.reset(new MainContextImpl(command_line, NULL)); // Populate this structure to customize CEF behavior. CefSettings settings; mainContext->PopulateSettings(&settings); settings.single_process = true; // Initialize CEF in the main process. CefInitialize(main_args, settings, app.get()); // Run the CEF message loop. This will block until CefQuitMessageLoop() is called. CefRunMessageLoop(); // Shut down CEF. CefShutdown();
MyClientApp主要集成CefBrowserProcessHandler和CefRequestContextHandler 实现页面加载、渲染和JS执行等
下边代码是头文件代码,具体的实现方式,参照CEF3的实例cefclient中ClientAppBrowser和 ClientAppRenderer。另外JS桥接口的调用,通过自定义实现CefV8Handler。在Renderer中的OnWebKitInitialized中注册桥方法。详见文章《CEF3开发者系列之JS与C++交互之二》
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. #ifndef CEF_TESTS_CEFCLIENT_RENDERER_CLIENT_APP_RENDERER_H_ #define CEF_TESTS_CEFCLIENT_RENDERER_CLIENT_APP_RENDERER_H_ #pragma once #include <set> #include "client_app.h" namespace client { // Client app implementation for the renderer process. class ClientAppRenderer : public ClientApp, public CefRenderProcessHandler { public: // Interface for renderer delegates. All Delegates must be returned via // CreateDelegates. Do not perform work in the Delegate // constructor. See CefRenderProcessHandler for documentation. class Delegate : public virtual CefBase { public: virtual void OnRenderThreadCreated(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefListValue> extra_info) {} virtual void OnWebKitInitialized(CefRefPtr<ClientAppRenderer> app) {} virtual void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser) {} virtual void OnBrowserDestroyed(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser) {} virtual CefRefPtr<CefLoadHandler> GetLoadHandler( CefRefPtr<ClientAppRenderer> app) { return NULL; } virtual bool OnBeforeNavigation(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, cef_navigation_type_t navigation_type, bool is_redirect) { return false; } virtual void OnContextCreated(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {} virtual void OnContextReleased(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {} virtual void OnUncaughtException(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context, CefRefPtr<CefV8Exception> exception, CefRefPtr<CefV8StackTrace> stackTrace) {} virtual void OnFocusedNodeChanged(CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefDOMNode> node) {} // Called when a process message is received. Return true if the message was // handled and should not be passed on to other handlers. Delegates // should check for unique message names to avoid interfering with each // other. virtual bool OnProcessMessageReceived( CefRefPtr<ClientAppRenderer> app, CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) { return false; } }; typedef std::set<CefRefPtr<Delegate> > DelegateSet; ClientAppRenderer(); private: // Creates all of the Delegate objects. Implemented by cefclient in // client_app_delegates_renderer.cc static void CreateDelegates(DelegateSet& delegates); // CefApp methods. CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE { return this; } // CefRenderProcessHandler methods. void OnRenderThreadCreated(CefRefPtr<CefListValue> extra_info) OVERRIDE; void OnWebKitInitialized() OVERRIDE; void OnBrowserCreated(CefRefPtr<CefBrowser> browser) OVERRIDE; void OnBrowserDestroyed(CefRefPtr<CefBrowser> browser) OVERRIDE; CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE; bool OnBeforeNavigation(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request, NavigationType navigation_type, bool is_redirect) OVERRIDE; void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE; void OnContextReleased(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE; void OnUncaughtException(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context, CefRefPtr<CefV8Exception> exception, CefRefPtr<CefV8StackTrace> stackTrace) OVERRIDE; void OnFocusedNodeChanged(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefDOMNode> node) OVERRIDE; bool OnProcessMessageReceived( CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) OVERRIDE; private: // Set of supported Delegates. DelegateSet delegates_; IMPLEMENT_REFCOUNTING(ClientAppRenderer); DISALLOW_COPY_AND_ASSIGN(ClientAppRenderer); }; } // namespace client #endif // CEF_TESTS_CEFCLIENT_RENDERER_CLIENT_APP_RENDERER_H_
至此完结。单进程适用于一些常规需求,比如通过前端的方式来实现界面。实现起来也不复杂,主要是通过本篇文章,再次熟悉下基本的进程模式及其作用。