CEF笔记:CEF重要的概念

重要的概念

在开发基于CEF3的应用程序前,需要理解一些重要的底层概念。

1. C++封装

libcef库使用C开发,导出的是一个C API。工程libcef_dll_wrapper是对这些C API的封装,与libcef库一起发布。这些c++代码通过工具translator自动生成。直接使用C API中以参看UsingTheCAPI部分。

2. 进程

CEF3基于多进程运行。主进程处理窗口的创建、UI及网络访问,主进程也叫浏览器进程。这个进程通常也是应用程序进程,大多数的逻辑运行在此进程下。渲染进程管理Blink渲染及JavaScript的运行。部分程序逻辑,如JavaScript的绑定、DOM的访问,也运行在渲染进程中。默认的进程模型会为每一个不同的跳转点(协议+域名,如http://www.baidu.com)创建一个新的渲染进程。在需要的时候,会创建一些其他类型的进程,如gpu进程。

默认情况下,主程序进程会被创建多次,作为非浏览器进程使用。即通过命令行传入不同标记给CefExecuteProcess函数实现非浏览器进程的创建。如果主程序的exe非常大,初始化需要很长的时间,或者主程序不适合作为非浏览器进程使用,那么可以提供一个单独的exe给子进程使用。这个可以通过配置项CefSettings.browser_subprocess_path实现。

CEF3创建的进程通过IPC技术进行交互。浏览器进程及渲染进程可以来回发送异步消息进行通信。渲染进程中的JavaScriptIntegration可以导出异步API,而这些API由浏览器进程进行具体处理(JavaScriptIntegration in the render process can expose asynchronous APIs that are handled in the browser process.)。

3. 线程

CEF3中每个进程都运行了多个线程。完整的线程列表可以查看枚举cef_thread_id_t。以下列举一些常用的线程。

  • TID_UI
    浏览器进程的主线程,如果使用CefSettings.multi_threaded_message_loop=false来调用CefInitialize(),则此线程同就应用程序主线程是同一个。

  • TID_IO
    浏览器进程用来处理IPC及网络消息的线程。

  • TID_FILE*
    浏览器进程用来与文件系统打交道的线程。阻塞操作应该在此线程或由客户程序创建的CefThread中运行。

  • TID_RENDERER
    渲染进程中的主线程。所有的Blink及V8交互必须在此线程中执行。

由于CEF的多线程特性,使用消息传递或锁定来保护数据成员不受多线程的访问是很重要的。CefPostTask系列函数支持在线程之间进行简单的异步消息传递。有关更多信息,请参见“Posting Tasks”部分。

当前线程可以使用CefCurrentlyOn()函数来验证。CEF样例程序中使用了以下辅助宏来帮助验证当前执行的是期望的线程类型。这此宏定义在include/wrapper/cef_helpers.h。

#define CEF_REQUIRE_UI_THREAD() DCHECK(CefCurrentlyOn(TID_UI));
#define CEF_REQUIRE_IO_THREAD() DCHECK(CefCurrentlyOn(TID_IO));
#define CEF_REQUIRE_FILE_BACKGROUND_THREAD() \
 DCHECK(CefCurrentlyOn(TID_FILE_BACKGROUND));
#define CEF_REQUIRE_FILE_USER_VISIBLE_THREAD() \
 DCHECK(CefCurrentlyOn(TID_FILE_USER_VISIBLE));
#define CEF_REQUIRE_FILE_USER_BLOCKING_THREAD() \
 DCHECK(CefCurrentlyOn(TID_FILE_USER_BLOCKING));
#define CEF_REQUIRE_RENDERER_THREAD() DCHECK(CefCurrentlyOn(TID_RENDERER));

为了支持异步访问一块代码,CEF提供了base::Lock和base::AutoLock类型,定义在include/base/cef_lock.h中,示例如下:

// Include the necessary header.
#include "include/base/cef_lock.h"

// Class declaration.
class MyClass : public CefBaseRefCounted {
 public:
  MyClass() : value_(0) {}
  // Method that may be called on multiple threads.
  void IncrementValue();
 private:
  // Value that may be accessed on multiple theads.
  int value_;
  // Lock used to protect access to |value_|.
  base::Lock lock_;
  IMPLEMENT_REFCOUNTING(MyClass);
};

// Class implementation.
void MyClass::IncrementValue() {
  // Acquire the lock for the scope of this method.
  base::AutoLock lock_scope(lock_);
  // |value_| can now be modified safely.
  value_++;
}

4. 引用计数

CEF框架下所有的类实现继承于CefBase[RefCounted|Scoped]接口,所以的指针实例通过CefRefPtr进行管理,CefRefPtr是一个智能指针,通过调用AddRef()及Release()进行引用计数的管理。最简单的示例如下:

class MyClass : public CefBaseRefCounted {
 public:
  // Various class methods here...

 private:
  // Various class members here...

  IMPLEMENT_REFCOUNTING(MyClass);  // Provides atomic refcounting implementation.
};

// References a MyClass instance
CefRefPtr<MyClass> my_class = new MyClass();

5. 字符串

CEF为字符串定义了自己的数据结构,原因如下:

  • libcef库与宿主程序可能使用不同的runtime库进行堆内存的管理。所有对象,包括字符串,需要使用相同的runtime库进行内存的创建与释放。
  • libcef库可以被编译支持不同字符类型的字符串(如UTF8,UTF16或者宽字符)。默认是UTF16,但你可以在cef_string.h修改,然后重新编译CEF来满足自己的需求。当使用宽字符时,要记住这会依赖具体的平台。

UTF16字符串的结构体:

typedef struct _cef_string_utf16_t {
  char16* str;  // Pointer to the string
  size_t length;  // String length
  void (*dtor)(char16* str);  // Destructor for freeing the string on the correct heap
} cef_string_utf16_t;

然后,起个别名如下:

typedef char16 cef_char_t;
typedef cef_string_utf16_t cef_string_t;

CEF提供了许多C API来操作字符串类型(mapped via #defines to the type-specific functions)。如下

  • cef_string_set
  • cef_string_clear
  • cef_string_cmp

CEF也提供了一些函数用于不同编码(ASCII, UTF8, UTF16及宽字符)字符串间的转换。具体可查看cef_string.h及cef_string_types.h。

C++下使用CefString来处理CEF的字符串会很方便。CefString提供了到std::string(UTF8)及std::wstring(宽字符)的自动转换。也可被用于对结构体类型cef_string_t的赋值。

  • 赋值或从std::string转换
std::string str = “Some UTF8 string”;

// Equivalent ways of assigning |str| to |cef_str|. Conversion from UTF8 will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromString(str);

// Equivalent ways of assigning |cef_str| to |str|. Conversion to UTF8 will occur if necessary.
str = cef_str;
str = cef_str.ToString();
  • 赋值或从std::wstring转换
std::wstring str = “Some wide string”;

// Equivalent ways of assigning |str| to |cef_str|. Conversion from wide will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromWString(str);

// Equivalent ways of assigning |cef_str| to |str|. Conversion to wide will occur if necessary.
str = cef_str;
str = cef_str.ToWString();
  • 字符串编码为ASCII时,可使用FromASCII()
const char* cstr = “Some ASCII string”;
CefString cef_str;
cef_str.FromASCII(cstr);
  • 一些结构体,如CefSettings,有一些cef_string_t成员。CefString可以简化它的赋值操作。
CefSettings settings;
const char* path =/path/to/log.txt”;

// Equivalent assignments.
CefString(&settings.log_file).FromASCII(path);
cef_string_from_ascii(path, strlen(path), &settings.log_file);

6. 命令行参数

CEF3和Chromium的很多功能可以通过命令行参数来控制。这些参数的格式为"–some_argument[=optional_param]",这些参数通过CefExecuteProcess()和CefMainArgs结构体来传递给CEF。

  • 在调用CefInitialize()前,可将CefSettings.command_line_args_disabled设置为true来禁用命令行参数。
  • 在宿主程序中,可以通过重载函数CefApp::OnBeforeCommandLineProcessing()来传递命令行参数
  • 可以通过重载函数CefBrowserProcessHandler::OnBeforeChildProcessLaunch()来向子进程传递特定的命令行参数

可以查看shared/common/client_switches.cc的注释获取更详细的信息

参考

https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-processes

你可能感兴趣的:(Web开发,CEF进程,CEF线程,CEF命令行,CEF字符串,CEF引用计数)