CEF架构

在这里插入图片描述

1、CEF架构的概述

1.1、背景:

CEF:chromium embedded Framework是由Marshall Greenblatt在08年成立的一个开源项目,目的是开发一个基于Google Chromium项目的Web browser控制器。CEF目前支持大多数编程语言和操作系统,并可以轻松的整合新的和已存在的应用。设计的目的也是为了效率/性能和方便使用。基础的框架包括了借助原生库的C/C++接口,这样将主机的应用与chromium和WebKit隔离开来。它提供了浏览器控制和主机应用程序(包括支持自定义插件、协议、JavaScript对象和JavaScript扩展)之间紧密的集成。主机应用程序可以有选择地控制资源加载、导航、菜单、打印等,当利用了相同的性能和Google Chrome浏览器具备的HTML5技术。

1.2、依赖/相关:

CEF项目依靠一堆其它由第三方维护的项目,主要又以下:

  • Chromium: 基础,网络堆栈,线程,消息机制,log,进程控制,生成Web browser。
  • WebKit: 提供DOM解析,布局,事件处理,渲染,HTML5JS的API。
  • **V8: ** JS引擎。 Skia:2D图形库。
  • Angle: 3D图形转换,和DirectX有关。
1.3、版本

3个版本:

  • CEF1: 单进程工具调用chromium Webkit API
  • CEF2: 多进程工具建立在Chromium browser
  • CEF3: ** 多进程工具调用Chromium**。
1.4、Content API

通用API用法:

所有版本的CEF都公开一个简单好用的API(让用户从ChromiumWebkit复杂的代码中分隔开来),详细如下:

  • 调用**CefInitialize()**对CEF初始化。
  • 调用CefRunMessageLoop() or **CefDoMessageLoopWork()**来在UI消息上处理事务。
  • 调用CreateBrowser() or CreateBrowserSync()并传递一个CefClient事例来创造一个browser窗口。
  • 在程序退出前调用**CefShutdown()**来关闭CEF。
1.5、CEF1

CEF1使用单进程架构,并直接将ChromiumWebkit整合到客户端应用程序中。单进程的优点包括了建设内存的使用和进一步与客户端应用的耦合。缺点有某些类型的加载内容性能低,和由于同进程中运行Flash插件的崩溃问题。

CEF1 API用法:

  • CefApp,此接口用来传递到CefInitialize(),和允许应用程序定制全局,如资源加载,代
    理。

  • CefClient,此接口用来传递到CefCreateBrowser() or CefCreateBrowserSync(),和充当单独CEF Browser事例和客户端应用程序的连接,也负责请求和显示处理的接口 。

  • CefBrowser,公开由浏览器提供的功能。包括前进后退导航,来源检索,加载请求等,一个CefBrowser可能有一到多个子类的CefFrame对象
    线程注意事项 。

  • CEF1包括UIIOFILE线程。 UI线程创建Browser窗口,用来所有与WebKitV8的交互,IO线程用来处理模式和网络请求。 FILE线程用于应用程序缓存和其他各种活动。

  • 当使用CEF1时,你应该记住以下线程考虑:

    • 不要在UI线程上执行会造成阻塞的操作。这将导致严重的性能问题。

    • 如果CefInitialize()通过值为falseCefSettings.multi_threaded_message_loop被调用,那么UI线程将和主线程一样。

    • 所有WebKitV8互为作用必须在UI线程上进行。因此,有些CEF API函数只能在UI线程上调用。有相同限制的功能会被相应地记录并关联到CEF的头文件里。

    • CefPostTask方法可以用来动态同步地进行不同线程里的任务。

      • 实施细则 :

        • CEF1主要有以下实现类:

          • CefContext 代表全局CEF内容。单个CefContext对象是由**CefInitialize()创建,由CefShutdown()**销毁 。
          • CefProcessCefContext创建和管理的CEF线程。
          • BrowserWebKitInit 管理全局WebKit环境作为Chromium WebKit API暴露。
          • WebViewHost 提供本地窗口“wrapper”工具给WebView。这个类扩展了,提供和在某些平台上弹出部件(如选择菜单)一样的功能,WebWidgetHost
          • CefBrowserImpl 实现CefBrowser的接口,创建WebViewHost,并提供了但个Browser实例的粘合代码。
          • BrowserWebViewDelegate 实现WebKit接口,提供CefBrowserImpl和底层的WebView之间
            的通信。
1.6、CEF2已经被取消了
1.7、 CEF3

CEF3借助Chromium Content API使用和Chromium Web 浏览器一样的多进程架构,相比于单进程的CEF1,他具有更多的优势。

  • 同时支持单进程和多进程模式。
  • 更多的代码分析通过Chromium浏览器 。
  • 改进的性能和由于代码路径的支持而减少的损坏 .
  • 新特性的快速获取 。
  • 大多数情况下,CEF3Chromium浏览器拥有相同的性能和稳定特征。
    • API用法 :
      • CEF3的初始化需要考虑多进程。在WindowsLinux上的所有进程享有相同的可执行文件,或选择使用单独的可执行文件的子进程(借助CefSettings.browser_subprocess_path的值)。在Mac OS-X的所有的子进程都必须绑推一个分开的app使用。这是必要的因为在Mac OS-X的子进程必须有一个不同的Info.plist文件。
      • CefExecuteProcess()函数是负责执行子过程的逻辑。它检查“类型”的命令行标志来决定目前正在执行的进程类型。如果被browser进程调用(识别为没有“类型”的命令行值)它会立即返回一个为-1值。如果被调用且认为是次级进程,它会阻塞直到该进程应该退出,然后返回进程的退出代码。如果执行browser进程,应用程序会继续在标准进程初始化,被描述为“通用API的用法”部分。将cef_app.h理解为一个完成的的功能初始化和用法的描述。将Wcefclient_win.cpp wWinMain功能视作一个窗口例程。
      • “单进程”命令行标志或者CefSettings.single_process的值可用在一个单一的进程中运行CEF3。这是有用于调试目的,但不建议用于生产。
        • 下面是主要CEF3接口和它们的用途的概述:
          • CefApp:此接口用来传递到CefInitialize(),和允许应用程序定制全局,如资源加载,代理。一些功能是由所有进程共享的,有些必须实现浏览器的过程中,必须在渲染过程中执行。见详情头文件的意见。
          • CefClient:此接口用来传递到CefCreateBrowser() or CefCreateBrowserSync(),和充当单独CEF Browser事例和客户端应用程序的连接,也负责请求和显示处理的接口。请求处理,显示处理等额外的接口,通过这个接口暴露。
          • CefBrowser:公开由浏览器提供的功能。包括前进后退导航,来源检索,加载请求等,一个CefBrowser可能有一到多个子类的CefFrame对象。在一个特定的过程或一个特定的线程必须调用一些方法,所以仔细阅读文档。
          • CefBrowserHost -:公开有关运行browser进程中唯一可用的browser窗口的功能。例如,检索本地父窗口句柄,或销毁browser窗口。
          • CefRenderProcessHandler : 公开WebKitV8对渲染进程中应用程序的集成能力。通过CefApp返回此对象的一个实例。
            • 实施细则CEF3主要有以下实现类:
              • CefMainDelegate 实现通用进程的引导逻辑。
              • CefContentClient 实现所有进程中共同的Content API回调。
              • CefContext 代表全局CEF内容在browser进程中。单个CefContext对象由**CefInitialize()
                建并由
                CefShutdown()**销毁。
              • CefBrowserMainParts 实现browser进程中的引导逻辑。
              • CefContentBrowserClient 实现browser进程的ContentAPI回调。
              • CefBrowserHostImpl 实现在browser进程中CefBrowserCefBrowserHost的接口。提供粘合代码和工具的借口来和RenderViewHost通信。
              • CefContentRendererClient 实现渲染进程中的ContentAPI回调。
              • CefBrowserImpl 实现渲染进程中的CefBrowser接口。提供粘合代码和工具的借口来和RenderView通信。
    • cef 是一个基于google chromiun的简单的框架。 它主要是作为一个内嵌浏览器嵌入到客户端应用程序中。可以再 http://cefbuilds.com 下载最新的编译版本。

2、总体框架预览

  • CEF 使用了多进程。主进程是“browser”进程。 而子进程是由rederes, plugins, GPU, 等组件创建。
  • CEF的所有进程中,都可以有多线程。CEF提供了函数和接口在不同的线程中来传递任务。
  • 一些回调方法和函数只能在特定线程和进程中使用。在使用API之前请确保仔细阅读注释。

3、源代码

  • cefsimple 工程初始化CEF并创建了一个简单的浏览器窗口。

    • 系统在入口点函数中(main或者wWinMain)函数中开启browser进程

    • 入口点函数:

      • 创建SimpleApp的实例,在这个类中保存process-level callbacks

      • 初始化CEF并开启消息循环。

    • 当CEF初始化完毕以后, **SimpleApp::OnContextInitialized()**会被调用。在这个方法中:

      • 创建一个单例的SimpleHandler

      • 由**CefBrowserHost::CreateBrowserSync()**创建一个浏览器窗口

    • 所有的浏览器共享同一个在SimpleHandlerSimpleHandler负责定制浏览器的行为并保存browser-related callbacks(loading状态,标题行为等)。

    • 当浏览器窗口被关闭的时候, SimpleHandler::OnBeforeClose() 被调用。当所有浏览器窗口被关闭,CEF消息循环退出。

  • 可以看下如下代码:

    int APIENTRY wWinMain(HINSTANCE hInstance
    				  , HINSTANCE hPrevInstance
    				  , LPTSTR lpCmdLine
    				  , int nCmdShow)
    {
    	CefMainArgs args(hInstance);
     
    	CefRefPtr<MySimpleApp> app(new MySimpleApp);
     
    	int exitCode = CefExecuteProcess(args, app, NULL);
    	if(exitCode >= 0) return exitCode;
    	CefSettings settings;
    	CefInitialize(args, settings, app, NULL);
     
    	CefRunMessageLoop();
    	CefShutdown();
    	return 0;
    }
    
  • 此时进程开启,但是没有任何窗口, 如果我们需要建立窗口需要如下:

    CefWindowInfo winInfo;
    winInfo.SetAsPopup(NULL, "myCefSimple");
     
    CefBrowserSettings browser_settings;
     
    CefRefPtr<CefClient> client(new MySimpleClient());
    std::string url = "file:///D:/project/github/cptf/resource/binding.html";
     
    CefBrowserHost::CreateBrowser(winInfo
    	, client.get()
    	, url
    	, browser_settings
    	, NULL);
    
  • 这样就显示出来窗口了。但是我们发现在关闭的时候进程没有关掉, 所以我们要做如下动作:

    class MySimpleClient : public CefClient
    				, public CefLifeSpanHandler
    void MySimpleClient::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
    	CEF_REQUIRE_UI_THREAD();
    	CefQuitMessageLoop();
    }
    

4、总结

  • CefAppCefClientCEF中最重要的两个类。CefExecuteProcessCefInitialize 是建立CefApp的两个最重要方法。
  • CreateBrowser 是创建浏览器窗口的最重要方法。
  • Cef支持各种语言和多种操作系统。在设计的时候充分考虑了性能和易用性。
  • cef核心功能提供了c和c++的接口。
  • cef提供了和主程序之间的通信能力(利用 custom plugins, protocols,javascrpit object 和 javascript extensions)。
  • 主应用程序可以选择性的使用控制 资源的加载,切换, context menus, printing等。

5、依赖

chromium
webkit
v8
skia
angle

6、线程注意事项

CEF线程有如下几种:

typedef enum {
	TID_UI,
	TID_DB,
	TID_FILE,
    TID_FILE_USER_BLOCKING,
	TID_PROCESS_LAUNCHER,
	TID_CACHE,
	TID_IO,
	TID_RENDERER,
} cef_thread_id_t;
  • 在使用线程的时候需要注意如下几点:
    • 千万不要阻塞UI线程。
    • UI线程会任务是主线程,当 CefSettings.multi_threaded_message_loop = false的时候。
    • 所有的webkitV8的交互必须用 TID_RENDERER线程。
    • CefPostTask 方法可以再不同线程中进行异步调用。

7、接口

  1. CefApp:此接口用来传递到CefInitialize(),和允许应用程序定制全局,如资源加载,代理。这些功能是由所有进程共享的,有些必须实现浏览器的过程中,必须在渲染过程中执行。见详情头文件的注释。
  2. CefClient:此接口用来传递到CefCreateBrowser() or CefCreateBrowserSync(),和充当单独CEF Browser事例和客户端应用程序的连接,也负责请求和显示处理的接口。请求处理,显示处理等额外的接口,通过这个接口暴露。
  3. CefBrowser:公开由浏览器提供的功能。包括前进后退导航,来源检索,加载请求等,一个CefBrowser可能有一到多个子类的CefFrame对象。在一个特定的过程或一个特定的线程必须调用一些方法,所以仔细阅读文档。
  4. CefBrowserHost :公开有关运行browser进程中唯一可用的browser窗口的功能。例如,检索本地父窗口句柄,或销毁browser窗口。
  5. CefRenderProcessHandler :公开WebKitV8对渲染进程中应用程序的集成能力。通过CefApp返回此对象的一个​​实例。

8、进程注意事项

CEF3使用了很多不同的进程:

  1. Broser process:这个进程可以认为是应该程序的主进程,当调用**CefInitialize()**的时候建立
  2. Render process:web容器(webkit和v8)在此进程中执行
  3. plugin process:插件(如 Flash)
  4. GPU process:GPU渲染进程
  5. Utility process: 各种其他任务在这个进程中跑。
  • 所有关于chrome 进程的资料可以在这里这里找到。

  • CefBrowserCefFramebrowserrender进程中都存在,并且传递一系列callbacks

  • CefProcessMessage能够在browser进中利用CefBrowser::SendProcessMessage 函数发出, 并且在CefClient::OnProcessMessageRecievedCefRenderProcessHandler::OnProcessMessageRecieved 接收。

9、重要的细节

CEF3 有如下几个比较重要的类:

  • CefMainDelegate :用于普通进程的逻辑过程
  • CefContentClient:在普通进程 展现Content Api的回调。
  • CefContext:在browser进程中,展现全局的CEF上下文。 一个单独的CefContext对象时由CefInitialize()建立,有CefShutdown销毁。
  • CefBrowserMainPartsbrowser 进程的逻辑。
  • CefContentBrowserClient:在browser 进程展现Content Api的回调。
  • CefBrowserHostImpl:是CefBrowserHost 的实现。
  • CefContentRendererClient:在render 进程中展现Content Api的回调。
  • CefBrowserImpl:CefBrowser的实现者。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐