本文基於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_
至此完結。單進程適用於一些常規需求,比如通過前端的方式來實現界面。實現起來也不復雜,主要是通過本篇文章,再次熟悉下基本的進程模式及其作用。