概述
CEF的详细关闭流程参考:https://blog.csdn.net/louis_815/article/details/76269057
通过上述博客我们首先需要明白WM_CLOSE,会在什么情况下触发:
- CefBrowserHost::CloseBrowser(false)
- 用户主动关闭窗口,发送WM_CLOSE
那么如果一个wnd拥有多个浏览器窗口(eg:多标签浏览器)。那么每关闭一次浏览器标签都会触发WM_CLOSE。此时我们就要决定何时以及怎么处理WM_CLOSE,抛弃还是交给系统处理??
为了方便处理WM_CLOSE,需要优化WM_CLOSE通知策略。对于开发者而言,CloseBrowser(false)产生的WM_CLOSE不需要关心,只需要关心用户到底是想关闭整个程序还是其中某一个网页?
优化策略:
- 将CloseBrowser(false)产生的WM_CLOSE丢给桌面窗口(对桌面没影响)
- 打标是否需要关闭应用程序,如果需要关闭的话,则在WM_CLOSE里面打标,并且关闭所有的网页窗口,当所有的网页窗口关闭完成的时候,投递WM_CLOSE给主窗口,然后关闭程序。
优化代码(一个网页窗口对应一个CefClient,外部管理CefClient):
创建Browser:
CefRefPtr<CBrowserHandler> CBrowserApp::CreateBrowser(HWND hParentWnd,std::string url)
{
.....
//每次人为CreateBroswer的时候创建一个对应的CBrowserHandler
//如果是通过网页内部打开或者ShowDevTool则共享CBrowserHandler,所以在共享句柄内部需要管理Browser
CefRefPtr<CBrowserHandler> simple_handler = new CBrowserHandler(hParentWnd);
CefBrowserSettings browser_settings;
CefBrowserHost::CreateBrowser(wnd_info, simple_handler, url, browser_settings, NULL);
return simple_handler;
}
管理类:
class CBrowserHandlerMgr
{
typedef std::map<int,CefRefPtr<CBrowserHandler> > vMapIDBrowsersHandler;
public:
...
void CBrowserHandlerMgr::AddBrowser(int nBrowserID, CefRefPtr<CBrowserHandler> pHandler)
{
//不能仅仅以pHandler为,因为可能一个Handler附加多个Browser
CCritical lk(m_cs);
if (!pHandler || !pHandler.get()) return;
bool bExist = false;
//理论上这句应该不成功
assert(m_vMapIDBrowsers.count(nBrowserID) <= 0);
m_vMapIDBrowsers[nBrowserID] = pHandler;
}
void CBrowserHandlerMgr::RemoveBrowser(int nBrowserID, CefRefPtr<CBrowserHandler> pHandler)
{
bool bEmpty = false;
{
CCritical lk(m_cs);
if (!pHandler || !pHandler.get()) return ;
m_vMapIDBrowsers.erase(nBrowserID);
bEmpty = m_vMapIDBrowsers.empty();
}
//如果打标为quit,则退出
if (m_hQuitWnd != NULL && bEmpty)
::PostMessage(m_hQuitWnd, WM_CLOSE, 0, 0);
}
void CBrowserHandlerMgr::CloseBrowser(int nBrowserID)
{
//防止长时间占用,导致死锁
CefRefPtr<CBrowserHandler> pHandler = NULL;
{
CCritical lk(m_cs);
if (m_vMapIDBrowsers.count(nBrowserID))
pHandler = m_vMapIDBrowsers[nBrowserID];
}
if (pHandler && pHandler.get())
{
//为了避免更换父窗口的时候闪现白屏,先隐藏窗口,在关闭
::ShowWindow(pHandler->GetBrowser(nBrowserID)->GetHost()->GetWindowHandle(), SW_HIDE);
::SetParent(pHandler->GetBrowser(nBrowserID)->GetHost()->GetWindowHandle(), GetDesktopWindow());
return pHandler->CloseBrowser(nBrowserID);
}
}
//如果HQuitWnd不为空,则退出应用程序
void CBrowserHandlerMgr::CloseAllBrowser(HWND hQuitWnd)
{
vMapIDBrowsersHandler vTempBrowsers;
{
CCritical lk(m_cs);
m_hQuitWnd = hQuitWnd;
vTempBrowsers = m_vMapIDBrowsers;
}
for (vMapIDBrowsersHandler::iterator it = vTempBrowsers.begin(); it != vTempBrowsers.end();++it)
{
if (it->second && it->second.get())
{
//由于一个窗口作为多个Browser的父窗口,为了防止WM_CLOSE捣鬼,需要更换brower的父窗口
//如果一个窗口仅仅作为一个Browser的父窗口,那么可以不需要切换brower的父窗口,正常处理即可
//为了避免更换父窗口的时候闪现白屏,先隐藏窗口,在关闭
::ShowWindow(it->second->GetBrowser(it->first)->GetHost()->GetWindowHandle(), SW_HIDE);
::SetParent(it->second->GetBrowser(it->first)->GetHost()->GetWindowHandle(), GetDesktopWindow());
it->second->CloseBrowser(it->first);
}
}
}
bool CBrowserHandlerMgr::IsCanClose()
{
CCritical lk(m_cs);
return m_vMapIDBrowsers.empty();
}
...
private:
vMapIDBrowsersHandler m_vMapIDBrowsers;
HWND m_hQuitWnd;//当需要关闭APP的时候指定,否则当浏览器数量为0的时候会误通知WM_CLOSE
CRITICAL_SECTION m_cs;
};
Render进程事件处理类:
class CBrowserHandler:public CefClient,public CefDisplayHandler,public CefLifeSpanHandler,public CefLoadHandler
{
void CBrowserHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
...
//增加到管理类
CBrowserHandlerMgr::instance()->AddBrowser(browser->GetIdentifier(),this);
// Add to the list of existing browsers.
m_vMapIDBrowser[browser->GetIdentifier()] = browser;
...
}
void CBrowserHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
CBrowserHandlerMgr::instance()->RemoveBrowser(browser->GetIdentifier(), this);
}
void CBrowserHandler::CloseBrowser(int nBrowserID)
{
CefRefPtr<CefBrowser> pBrowser = GetBrowser(nBrowserID);
if (pBrowser && pBrowser.get())
{
pBrowser->GetHost()->CloseBrowser(false);
}
}
bool CBrowserHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
//释放Browser引用
int nBrowserID = browser->GetIdentifier();
m_vMapIDBrowser[nBrowserID] = NULL;
m_vMapIDBrowser.erase(nBrowserID);
return false;
}
}
主窗口WM_CLOSE消息处理
void CCefDemoDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//因为CefClient关闭的时候更改了窗口句柄,所以投递的WM_CLOSE当前窗口并不会收到,此时我们可能只是关闭某个标签并不需要关闭浏览器
if (m_browser_app && m_browser_app.get() && m_browser_app->CloseAllBrowser(GetSafeHwnd()) && !m_browser_app->isCanClosed())
return;
CDialogEx::OnClose();
}
最后
以上就是成就豌豆为你收集整理的CEF 关闭流程优化的全部内容,希望文章能够帮你解决CEF 关闭流程优化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复