我是靠谱客的博主 成就豌豆,最近开发中收集的这篇文章主要介绍CEF 关闭流程优化,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

CEF的详细关闭流程参考:https://blog.csdn.net/louis_815/article/details/76269057

通过上述博客我们首先需要明白WM_CLOSE,会在什么情况下触发:

  1. CefBrowserHost::CloseBrowser(false)
  2. 用户主动关闭窗口,发送WM_CLOSE

那么如果一个wnd拥有多个浏览器窗口(eg:多标签浏览器)。那么每关闭一次浏览器标签都会触发WM_CLOSE。此时我们就要决定何时以及怎么处理WM_CLOSE,抛弃还是交给系统处理??

为了方便处理WM_CLOSE,需要优化WM_CLOSE通知策略。对于开发者而言,CloseBrowser(false)产生的WM_CLOSE不需要关心,只需要关心用户到底是想关闭整个程序还是其中某一个网页?

优化策略:

  1. 将CloseBrowser(false)产生的WM_CLOSE丢给桌面窗口(对桌面没影响)
  2. 打标是否需要关闭应用程序,如果需要关闭的话,则在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 关闭流程优化所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部