前端开发框架Chromium Embedded Framework (CEF)再探

简介

在此之前,前端开发框架Chromium Embedded Framework (CEF)入门介绍了如何在Windows平台上搭建CEF的开发环境,并就官方的sample源码进行了简单的剖析,但是还不够,所以笔者继续对CEF进行了一番探索

线程模型

CEF中每个进程都运行多个线程,其中常用的线程有:

  • TID_UI:UI线程是浏览器进程的主线程,如果在调用CefInitialize()CefSettings.multi_threaded_message_loop设置为false,那么该线程也是主应用程序线程
  • TID_IO:IO线程在浏览器进程中用于处理IPC和网络消息
  • TID_FILE_*:这会是一系列类型的线程,在浏览器进程中用于与文件系统进行交互工作。阻塞的操作只能在此线程或者客户端创建的CefThread线程中执行
  • TID_RENDERER:该线程是渲染器进程中的主线程,所有BlinkV8的交互都必须在该线程中进行

开发时需要验证当前线程为哪一类型可以使用CEF定义的宏:

#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应用程序代码结构

每个CEF程序都有大致相同的结构,需要完成以下几点工作:

  • 初始化CEF并运行子进程逻辑或者进行CEF消息循环
  • 提供CefAPP子类,实现特定于每种类型的进程的回调函数
  • 提供CefClient子类,实现处理与之绑定的浏览器实例特定的回调函数
  • 通过CefBrowserView::CreateBrowserView()或者CefBrowserHost::CreateBrowser()创建浏览器实例,并使用CefLifeSpanHandler对该浏览器实例的生命周期进行管理。CEF框架通过你提供并重写CefClientGetLifeSpanHandler()方法获取CefLifeSpanHandler

初始化时命令行配置信息

初始化首先需要指定命令行的配置信息,CEF在启动子进程时,这些配置信息将会作用于子进程。这些信息必须通过CefMainArgs结构传递给CefExecuteProcess()CefMainArgs的获取是特定于平台的

在Linux和MacOS上,通过入口函数的argcargv值获取

CefMainArgs main_args(argc, argv);

在Windows上,通过入口函数的hInstance获取

CefMainArgs main_args(hInstance);

前端开发框架Chromium Embedded Framework (CEF)入门文章中有介绍Windows上CEF程序是以单可执行文件运行的,即子进程和主进程都运行同一个可执行程序,在入口函数处区分不同的进程类型,执行不同的逻辑,这是Windows和Linux所支持的,但是这在MacOS上不好使

这是因为在MacOS平台上一个APP bundle会涉及一些安全问题,如果采用和MacOS和Linux的程序架构,会导致主进程和子进程拥有相同的安全限制,但是像渲染进程这样的子进程往往会运行js这样的代码,容易受到攻击,让一个容易受到攻击的进程拥有和主进程相同的安全限制不是一个很好的主意

所以在MacOS上,主进程和子进程不共用一个可执行程序,主进程拥有较高的权限,子进程则只有满足其正常运行的最低权限

CefAPP和CefClient

CefAPP和CefClient在 前端开发框架Chromium Embedded Framework (CEF)入门中已经有过介绍

CefAPP提供对某个进程特定回调的访问,你需要实现CefAPP的子类,主进程可以通过CefInitialize()绑定一个CefApp,子进程则可以通过CefExecuteProcess()绑定一个CefApp,其重要回调包括:

  • OnBeforeCommandLineProcessing():CEF框架在启动子进程前,通过这个回调提供修改子进程命令行参数的机会。这里需要留意的是不要去引用该函数的入参
  • GetBrowserProcessHandler():CEF用于获取特定于浏览器进程的处理程序——CefBrowserProcessHandler
  • GetRenderProcessHandler():CEF用于获取特定于渲染进程的处理程序——GetRenderProcessHandler。JavaScript相关的回调和进程消息都和该处理程序相关

CefClient提供对浏览器实例特定回调的访问,你需要实现CefClient的子类,并在创建浏览器实例时,将你实现的子类与创建的浏览器实例进行绑定。CefBrowserView::CreateBrowserView()CefBrowserHost::CreateBrowser()返回的对象就是一个浏览器实例,在调用这两个API时就需要传入实现的CefClient子类与之绑定

CefClient重要的回调包括:

  • Get*Handler():众多返回Handler的方法,返回的每个Handler用于对与之绑定的浏览器实例的控制。例如GetDisplayHandler()返回的CefDisplayHandler则可以控制改变浏览器实例的title
  • OnProcessMessageReceived():当收到渲染进程的IPC消息时,回调该函数

浏览器的生命周期

一个浏览器实例的生命周期从调用CefBrowserHost::CreateBrowser()CefBrowserHost::CreateBrowserSync()开始,该逻辑一般在回调函数CefBrowserProcessHandler::OnContextInitialized()中进行。在Windows平台,也有可能在WM_CREATE中进行

浏览器实例的生命周期通过CefLifeSpanHandler进行管理,CEF框架如何获取CefLifeSpanHandler呢?正如我们前面所说,通过和该浏览器实例绑定的CefClient获取。CefClient提供GetLifeSpanHandler()返回一个CefLifeSpanHandler。当然,返回的CefLifeSpanHandler是你实现的CefLifeSpanHandler子类的实例

CefLifeSpanHandler提供的重要回调包括:

  • OnAfterCreated():CEF框架将在创建浏览器后回调。在这里,你可以保存对该浏览器对象CefBrowser的引用
  • OnBeforeClose():CEF将在浏览器销毁之前回调。在此回调之后,你不能尝试在该浏览器对象CefBrowser上调用任何方法(IsValidGetIdentifierIsSame除外,这些方法还可以调用)

公众号名称:zl.rs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zl.rs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值