马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 KenTien 于 2025-2-17 15:01 编辑
3.1.3. 第一个类:CefApp操作系统调用完程序的入口函数后,CEF 框架就通过其自身的消息循环机制接管了接下来的执行工作,在上一小节中我们提到了CefApp对象,并且把这个对象传递给了 CEF 的 Initialize方法,CEF 框架收到这个对象之后,会把浏览器进程的一些逻辑交给CefApp对象执行,也就是说CefApp对象就是我们浏览器等进程的入口程序。先看头文件的代码:
[C++] 查看源码 复制代码 /*CefApp接口提供了指定进程的回调访问*/
class CefApp : public virtual CefBaseRefCounted {
public:
//即将执行命令行
virtual void OnBeforeCommandLineProcessing(const CefString& process_type,
CefRefPtr<CefCommandLine> command_line) {}
// 在CEF应用程序中注册自定义的HTTP协议和URL。
virtual void OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar) {}
//返回浏览器进程特定功能的处理程序。在资源进程中的多个线程上调用此方法。
virtual CefRefPtr<CefResourceBundleHandler> GetResourceBundleHandler() {
return nullptr;
}
//返回浏览器进程特定功能的处理程序。在浏览器进程中的多个线程上调用此方法。
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() {
return nullptr;
}
//返回浏览器进程特定功能的处理程序。在渲染器进程中的多个线程上调用此方法。
virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() {
return nullptr;
}
};
我们可以这样理解:一个CefApp对应了一个进程,而一个进程可以是Browser Process,可以是Renderer Process。因此,CefApp提供了GetBrowserProcessHandler和GetRendererProcessHandler来分别在相关进程中获取对应的handler。那么CEF是如何将我们的CefApp实例关联到CEF运行中的呢?
[C++] 查看源码 复制代码 context->Initialize(main_args, settings, app, sandbox_info);
3.1.4. 第二个类:CefClientCefClient接口提供对特定于浏览器等实例的回调的访问。一个CefClient实例可以在任意数量的浏览器之间共享。重要的回调包括: 所有的Handler,例如浏览器的生命周期,上下文菜单,对话框,显示通知,拖动事件,焦点事件,键盘事件等。大多数处理程序是可选的。请参阅cef_client.h中的文档,以了解不实施特定处理程序的副作用(如果有)。 从渲染过程中接收到IPC消息时调用的OnProcessMessageReceived。有关更多信息,请参见“进程间通信”部分。 首先需要解释一下什么什么是特定浏览器实例,实际上,指的是以下过程产生的浏览器实例:
[C++] 查看源码 复制代码 CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
handler, url, browser_settings, nullptr, nullptr,
new SimpleBrowserViewDelegate());
// 或
CefBrowserHost::CreateBrowser(window_info, handler, url, browser_settings,
nullptr, nullptr);
通过上述两种方式创建的浏览器实例,是一个概念上的实例,并不是指你能看得到的浏览器的窗口,窗口只是浏览器实例的宿主而已。而浏览器中发生的事件,例如:生命周期的变化,对话框等,都只会通过CefClient中返回的各种类型Handler以及这些Handler接口实例提供的方法回调。 下面是CefClient的声明:
[C++] 查看源码 复制代码 class CefClient : public virtual CefBaseRefCounted {
public:
virtual CefRefPtr<CefAudioHandler> GetAudioHandler() { return nullptr; }
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() {
return nullptr;
}
virtual CefRefPtr<CefDialogHandler> GetDialogHandler() { return nullptr; }
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() { return nullptr; }
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() { return nullptr; }
virtual CefRefPtr<CefDragHandler> GetDragHandler() { return nullptr; }
// ...... 还有很多的Handler
}
在这个CefClient提供了很多GetXXXHandler方法,这些方法会在合适的时候,被CEF调用以得到对应的Handler,然后再调用返回的Handler中的方法。例如,HTML页面中的Title发生变化的时候,就会调用CefClient::CefDisplayHandler()得到一个CefDisplayHandler实例,然后再调用其中的CefDisplayHandler::OnTitleChange,而这些过程不是我们调用的,而是CEF框架完成的。只是具体的实现有我们客户端代码编写。 那么现在思考一下,为什么会有这个CefClient呢?在本人看来主要是如下的理由: 在CefClient中各种回调的事件,本质上发生的地方是渲染进程。因为每当一个浏览器实例(不是浏览器进程)创建的时候,会有一个对应的渲染进程创建(也可能由于配置,而共用一个,这里先认为默认多个一对一)。渲染进程中发生的各种V8事件、下载事件,显示事件等触发后,会通过进程间通讯给到浏览器进程,然后在浏览器进程中找到与之相关的CefClient,然后从CefClient中找到对应的Handler,回调Handler对应的方法。 也就是说,将在渲染进程发生的事件,用在浏览器进程中的CefClient一定的抽象映射,而不是直接在浏览器进程处理器中进行,因为一个浏览器进程可能会创建多个渲染进程,让CefClient作为中间层避免耦合。
当然,文档也为我们指出,CefClient实例与浏览器实例可以不是一一对应的,多个浏览器实例可以共享一个CefClient,如此一来我们也可以总结关于CefClient的一点:非必要情况,不要编写具有状态的CefClient。
|