KenTien 发表于 2025-2-17 14:37:09

CEF3专辑-基础篇-04-代码初试2

本帖最后由 KenTien 于 2025-2-17 15:01 编辑

3.1.3. 第一个类:CefApp操作系统调用完程序的入口函数后,CEF 框架就通过其自身的消息循环机制接管了接下来的执行工作,在上一小节中我们提到了CefApp对象,并且把这个对象传递给了 CEF 的 Initialize方法,CEF 框架收到这个对象之后,会把浏览器进程的一些逻辑交给CefApp对象执行,也就是说CefApp对象就是我们浏览器等进程的入口程序。先看头文件的代码:
/*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运行中的呢?
context->Initialize(main_args, settings, app, sandbox_info);

3.1.4. 第二个类:CefClientCefClient接口提供对特定于浏览器等实例的回调的访问。一个CefClient实例可以在任意数量的浏览器之间共享。重要的回调包括:所有的Handler,例如浏览器的生命周期,上下文菜单,对话框,显示通知,拖动事件,焦点事件,键盘事件等。大多数处理程序是可选的。请参阅cef_client.h中的文档,以了解不实施特定处理程序的副作用(如果有)。从渲染过程中接收到IPC消息时调用的OnProcessMessageReceived。有关更多信息,请参见“进程间通信”部分。首先需要解释一下什么什么是特定浏览器实例,实际上,指的是以下过程产生的浏览器实例:
    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的声明:
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。


页: [1]
查看完整版本: CEF3专辑-基础篇-04-代码初试2