背景
cef是一種跨平臺(tái)的框架,屬于chrome內(nèi)核,可以用來(lái)顯示web相關(guān)頁(yè)面。目前在咚咚工作臺(tái)上使用,顯示聊天框,歷史消息,插件頁(yè)面等等。之前只是在win平臺(tái)上使用,在今年開(kāi)發(fā)mac版本商家咚咚過(guò)程中,完成了mac版本的適配,積累了一些在兩個(gè)平臺(tái)上的使用方法,避免以后再踩坑。
?
一. 庫(kù)和資源文件
基本的庫(kù)文件包含了多個(gè)文件夾和資源文件,這些是運(yùn)行cef時(shí)必須要依賴(lài)的,因此需要打包在安裝包中。
| win | mac | 說(shuō)明 |
|---|---|---|
| include文件夾 | include文件夾 | cef內(nèi)部接口對(duì)外暴露的頭文件 |
| libcef_dll文件夾 | libcef_dll文件夾 | cef對(duì)外的wrapper庫(kù)相關(guān)的cc文件 |
| Resources文件夾 | Resources文件夾 | cef依賴(lài)的pak文件 |
| libcef.dll | Chromium Embedded Framework | cef核心庫(kù)文件 |
| 其它dll庫(kù),libEGL.dll等 | 其它dylib庫(kù),libEGL.dylib等 | cef依賴(lài)動(dòng)態(tài)庫(kù)文件 |
| 其它bin文件,natives_blob.bin等 | 無(wú) | cef依賴(lài)bin文件 |
表1.1 cef依賴(lài)文件
以上是cef運(yùn)行時(shí)所依賴(lài)的所有文件,其中include和libcef_dll, 可以組合生成一個(gè)lib靜態(tài)庫(kù),一般名稱(chēng)為libcef_dll_wrapper.lib,在其它模塊中需要依賴(lài)該cef頭文件時(shí),導(dǎo)入lib庫(kù)即可。
在升級(jí)cef版本的過(guò)程中,只需要替換替換對(duì)應(yīng)的文件夾中的文件,就可以升級(jí)到對(duì)應(yīng)的版本,目前最新的100以上的版本,基本都是這些固定的文件格式。
如果添加了新的文件或者依賴(lài)庫(kù),則需要添加對(duì)應(yīng)的庫(kù)和資源。
?
二. 文件路徑查找
資源文件,需要設(shè)定路徑才能找到對(duì)應(yīng)的文件,在CefSettings指定對(duì)應(yīng)的路徑。
| 路徑 | win | mac |
|---|---|---|
| resource目錄(pak文件) | resources_dir_path | framework_dir_path/main_bundle_path |
| locales目錄 | locales_dir_path | framework_dir_path/main_bundle_path |
| 子進(jìn)程路徑 | browser_subprocess_path | browser_subprocess_path |
表2.1 cef資源路徑設(shè)置
2.1 對(duì)于資源路徑,win版本可以放到同一目錄下面,例如在安裝目錄下,新建一個(gè)CEF目錄,專(zhuān)門(mén)用來(lái)放置資源文件:

2.2 dll庫(kù)則在主安裝目錄下面:

2.3 mac版本的目錄文件基本是固定的:
Chromium Embedded Framework.framework
一般可以放置到app包的頂層或者Frameworks目錄下面

?
以上的路徑,都需要明確指定絕對(duì)路徑,即完整的訪問(wèn)路徑資源,不能使用相對(duì)路徑,比如../../../之類(lèi)的,因此在程序內(nèi)部,需要專(zhuān)門(mén)封裝查找資源路徑的方法,這樣在應(yīng)用安裝到用戶(hù)電腦上,才能準(zhǔn)確找到對(duì)應(yīng)的資源。
若資源文件無(wú)法找到,則在后續(xù)的初始化,第一步就會(huì)報(bào)錯(cuò),只有找到文件,才能進(jìn)行后續(xù)的步驟。
?
三. 主進(jìn)程初始化
主進(jìn)程初始化分為多個(gè)步驟:

?
3.1 加載cef動(dòng)態(tài)庫(kù)
如果將libcef庫(kù)放置在標(biāo)準(zhǔn)目錄下面,即win是exe同級(jí)目錄,則無(wú)需查找動(dòng)態(tài)庫(kù),系統(tǒng)可以自行查找到,如果放到其它目錄,則需要采用動(dòng)態(tài)加載庫(kù)的方式。
mac可以使用cef提供的標(biāo)準(zhǔn)方法:
CefScopedLibraryLoader library_loader;
library_loader.LoadInMain()
來(lái)直接加載主進(jìn)程庫(kù)。
若采用顯示路徑來(lái)加載的方式,可以指定
Chromium Embedded Framework.framework下面的Chromium Embedded Framework,整個(gè)的完整路徑
然后使用cef提供的方法:cef_load_library,加載指定路徑下的主進(jìn)程文件。
加載主庫(kù)必須成功,失敗則直接返回,后續(xù)一切步驟都依賴(lài)主進(jìn)程的加載完成。
3.2 獲取啟動(dòng)參數(shù)
啟動(dòng)的時(shí)候,都需要使用CefMainArgs,來(lái)獲取命令行的參數(shù)
| win | mac |
|---|---|
| HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); CefMainArgs main_args(hInstance); CefRefPtr command_line = CefCommandLine::CreateCommandLine(); command_line->InitFromString(::GetCommandLineW()); | CefMainArgs main_args(argc, argv); CefRefPtr command_line = CefCommandLine::CreateCommandLine(); command_line->InitFromArgv(argc, argv); |
| 使用系統(tǒng)方法,直接獲取命令行參數(shù) | 從main方法傳遞argc, argv,從中獲取命令參數(shù) |
表3.1 cef獲取啟動(dòng)命令
接著生成 CefRefPtr app,這個(gè)類(lèi)繼承自cef的基本app類(lèi):CefApp
繼承ClientApp,來(lái)生成主進(jìn)程方法:
app = new ClientAppBrowser();
3.3 設(shè)置其它參數(shù)
設(shè)置日志級(jí)別:settings.log_severity
日志目錄:settings.log_file
瀏覽器版本和說(shuō)明:settings.user_agent
調(diào)試時(shí)的端口:settings.remote_debugging_port
3.4 主進(jìn)程初始化
直接調(diào)用CefInitialize(main_args, settings, app.get(), sandbox_info)
傳入命令,設(shè)置選項(xiàng),app實(shí)例,sandbox的內(nèi)容,通常來(lái)說(shuō)sandbox為空,不啟用即可
返回成功可以進(jìn)行后續(xù)步驟,失敗表示主進(jìn)程啟動(dòng)異常,后續(xù)也無(wú)法顯示頁(yè)面。
?
四.渲染進(jìn)程生成
渲染進(jìn)程,可以采用和主進(jìn)程合并模式,也可以采用單獨(dú)的進(jìn)程。一般比較大型或者復(fù)雜的項(xiàng)目,都需要單獨(dú)的進(jìn)程,并為了不同的頁(yè)面,還需要啟動(dòng)多個(gè)子進(jìn)程。
啟動(dòng)流程和主進(jìn)程類(lèi)似,先加載子進(jìn)程路徑,再獲取命令行參數(shù),繼承CefApp啟動(dòng)實(shí)例,最后再初始化。
進(jìn)程名稱(chēng),win可以自己定義,mac有嚴(yán)格限制,必須以xxx Helper結(jié)尾,否則找不到對(duì)應(yīng)的渲染子進(jìn)程。
| win | mac |
|---|---|
| 獨(dú)立的exe應(yīng)用 | 獨(dú)立的app應(yīng)用 |
| 子進(jìn)程名稱(chēng).exe | 子進(jìn)程核心應(yīng)用:xxx Helper.app 子進(jìn)程GPU應(yīng)用:xxx Helper (GPU) 子進(jìn)程Plugin應(yīng)用:xxx Helper (Plugin) 子進(jìn)程Render應(yīng)用:xxx Helper (Renderer) |
表1.1 cef子進(jìn)程名稱(chēng)
4.1 子進(jìn)程的路徑,理論上可以設(shè)置到專(zhuān)門(mén)的目錄下面,但也可以讓系統(tǒng)去默認(rèn)路徑查找,即和主應(yīng)用同級(jí)別即可。
加載cef資源的時(shí)候,win版本在主應(yīng)用同級(jí)別的exe,不用專(zhuān)門(mén)的方法去加載。
mac版本需要使用cef的專(zhuān)用方法:
CefScopedLibraryLoader library_loader;
library_loader.LoadInHelper()
若是需要專(zhuān)門(mén)的路徑,則仍然使用cef_load_library去加載庫(kù)的絕對(duì)路徑,加載成功后,才能正常初始化子進(jìn)程。
?
4.2 在最后初始化時(shí),可以直接調(diào)用cef的方法:
CefExecuteProcess(main_args, app, nullptr)
返回值大于0,表示啟動(dòng)子進(jìn)程已經(jīng)成功,可以開(kāi)啟渲染頁(yè)面等操作。
返回值小于0,表示啟動(dòng)子進(jìn)程失敗,頁(yè)面將無(wú)法進(jìn)行渲染,但是不影響主進(jìn)程的正常運(yùn)行。
子進(jìn)程實(shí)現(xiàn)的最好方式,是將所有的依賴(lài)文件,都集成在同一個(gè)模塊工程中,不依賴(lài)其它的模塊,這樣在編譯運(yùn)行的時(shí)候,就不會(huì)由于依賴(lài)過(guò)多導(dǎo)致各種錯(cuò)誤。
?
五. 消息循環(huán)
不同的系統(tǒng)版本中,采用不同的消息循環(huán)模式,需要和主應(yīng)用的消息循環(huán)合作運(yùn)行。
5.1 在win版本中,可以通過(guò)設(shè)置multi_threaded_message_loop為true的參數(shù),開(kāi)啟瀏覽器多線程模式。能夠和主應(yīng)用的主消息循環(huán)并行處理運(yùn)行。

如圖所示,win應(yīng)用中,在main方法中需要開(kāi)啟主界面的消息循環(huán),這里的消息循環(huán)就一直在UI主線程中運(yùn)行,直到應(yīng)用退出,消息循環(huán)主線程才會(huì)結(jié)束退出。
另外在cef的瀏覽器主進(jìn)程中,也同時(shí)開(kāi)啟多線程消息循環(huán),此時(shí)Browser獨(dú)享自己的消息循環(huán)線程,和主消息循環(huán)互不影響,可以實(shí)時(shí)的收到界面的點(diǎn)擊響應(yīng)和顯示web界面等。
?
5.2 在mac版本中,有兩種cef消息循環(huán)方式
第一種:直接將主應(yīng)用的消息循環(huán)設(shè)置為cef的消息循環(huán),即所有的消息接收和傳遞,都在CefRunMessageLoop()中進(jìn)行,此時(shí)cef的消息循環(huán)占據(jù)了主導(dǎo)地位,主界面的按鈕點(diǎn)擊等操作,都需要cef拋出并提交給主線程。
第二種:當(dāng)mac中采用了Qt等框架時(shí),它本身有自己的消息主循環(huán),而cef在mac中無(wú)法再開(kāi)啟自己的消息主循環(huán),也不支持采用多線程機(jī)制單獨(dú)運(yùn)行。因此采用了將cef消息循環(huán)嵌入到Qt的消息循環(huán)中的方式,相當(dāng)于時(shí)間片分割方式,到了定時(shí)時(shí)間,去專(zhuān)門(mén)處理cef的事件響應(yīng)。

主循環(huán)消息啟動(dòng)后,生命周期跟隨主應(yīng)用,一直到應(yīng)用退出才會(huì)結(jié)束。此時(shí)定一個(gè)時(shí)間片分割,時(shí)間自定義,可以50幀也就是每隔20ms處理一次。
當(dāng)定時(shí)周期到的時(shí)候,就執(zhí)行cef的消息循環(huán)處理,在CefDoMessageLoopWork()中去處理cef的具體事件響應(yīng)。
另外在cef本身的回調(diào)中,也可以使用onScheduleMessageLoopWork()中拿到事件,然后到cef事件循環(huán)中去處理。
這樣主循環(huán)和cef自己的的循環(huán),就不斷運(yùn)行起來(lái),在用戶(hù)使用時(shí),感覺(jué)不到這個(gè)細(xì)微的延時(shí)差距,主界面和cef界面基本都是在同時(shí)響應(yīng)。
?
六.窗口適配
在界面顯示過(guò)程中,打開(kāi)網(wǎng)頁(yè)或者本地頁(yè)面時(shí),通常需要重新修改窗口尺寸。在不同的版本需要不同的處理方式:
| 功能 | win | mac |
|---|---|---|
| 獲取窗口id | (CefWindowHandle)this->winId() | (CefWindowHandle)this->winId() |
| 改變窗口大小 | ::MoveWindow(hwnd, rect.x(), rect.y(), rect.width(), rect.height(), true) | [nsview setFrameSize:NSMakeSize(rect.width(), rect.height())] |
| 獲取窗口句柄 | browser->GetHost()->GetWindowHandle() | browser->GetHost()->GetWindowHandle() browser->GetHost()->GetOpenerWindowHandle() |
表6.1 cef窗口適配
在創(chuàng)建一個(gè)瀏覽器窗口時(shí),需要調(diào)用SetAsChild設(shè)置瀏覽器的子窗口,這里首先獲取當(dāng)前生成的窗口id,可以直接用CefWindowHandle來(lái)進(jìn)行轉(zhuǎn)換。
在重繪窗口大小的時(shí)候,需要移動(dòng)窗口,調(diào)整高和寬,這里cef沒(méi)有提供通用的方法,只能使用不同平臺(tái)的原生方法,因此調(diào)用了操作系統(tǒng)的系統(tǒng)接口。
獲取窗口可以直接使用cef內(nèi)部封裝好的GetWindowHandle,這個(gè)是通用的接口,需要在UI線程中調(diào)用,但是在一些特殊的web窗口中,需要使用GetOpenerWindowHandle來(lái)獲取非pop窗口的句柄,從而操作窗口界面正常顯示。
?
七.版本區(qū)分
7.1 操作系統(tǒng)版本區(qū)分可以直接使用開(kāi)源庫(kù)的方法:
win系統(tǒng):Q_OS_WIN
mac系統(tǒng):Q_OS_MAC
當(dāng)需要區(qū)分使用不同操作系統(tǒng)的接口,或者調(diào)用對(duì)應(yīng)系統(tǒng)的原生方法時(shí),需要使用該宏定義進(jìn)行區(qū)分處理。
7.2 cef版本區(qū)分,可以使用cef_version中的定義:
#define CEF_VERSION_MAJOR 106
#define CEF_VERSION_MINOR 1
#define CEF_VERSION_PATCH 0
#define CEF_COMMIT_NUMBER 2678
通常來(lái)說(shuō),若應(yīng)用程序中,同時(shí)存在多個(gè)cef版本,比如89,106等,主要是主版本號(hào)不同,則直接判斷CEF_VERSION_MAJOR的數(shù)值即可,另外的三個(gè)小版本可以不做過(guò)多關(guān)注,若大版本相同,則需要進(jìn)行小版本的詳細(xì)區(qū)分。
不同的cef大版本,接口基本是一樣的,但是當(dāng)相差幾十個(gè)版本的cef庫(kù)時(shí),則會(huì)出現(xiàn)接口參數(shù)變更,或者名稱(chēng)變化,新增部分接口等差異點(diǎn),則需要在不同版本進(jìn)行對(duì)應(yīng)的處理和適配。
?
結(jié)尾
使用cef在win和mac上用的同一套文件,利用Qt的宏定義對(duì)平臺(tái)進(jìn)行了區(qū)分,利用cef版本對(duì)不同版本進(jìn)行兼容,因此同樣的源代碼文件,在兩個(gè)平臺(tái)都可以運(yùn)行,不同的cef版本也可以同時(shí)運(yùn)行,比較方便維護(hù)升級(jí)。
后續(xù)可以繼續(xù)完善,形成更多不同的功能,并進(jìn)一步拆分成獨(dú)立的模塊,讓大家少走一些彎路。
審核編輯 黃宇
-
Win
+關(guān)注
關(guān)注
0文章
66瀏覽量
28897 -
Mac
+關(guān)注
關(guān)注
0文章
1124瀏覽量
54585 -
CEF
+關(guān)注
關(guān)注
0文章
38瀏覽量
18779
發(fā)布評(píng)論請(qǐng)先 登錄
在ESP32平臺(tái)使用以太網(wǎng)DM9051ANX自帶的MAC地址
在iMX6ULL上設(shè)置MAC地址,啟動(dòng)后無(wú)法獲得相同的mac id?
通過(guò)注冊(cè)表和Technitium MAC Address Changer修改MAC地址(支持W5500模塊及通用網(wǎng)卡)
通過(guò)注冊(cè)表和Technitium MAC Address Changer修改MAC地址(支持W5500模塊及通用網(wǎng)卡)
如何修復(fù)S32G gmac的mac地址?
在Mac上使用Docker構(gòu)建noVNC環(huán)境并運(yùn)行MyCobot

淺析cef在win和mac上的適配
評(píng)論