TeamTalk客户端源码分析一

版权声明:本文为博主原创文章,如需转载请注明出处 https://blog.csdn.net/bajianxiaofendui/article/details/84842370

     TeamTalk的PC客户端是c++语言实现的,整个代码由duilib,gifsmiley,httpclient,libogg,modules等工程构成。
1)duilib,界面库
2)gifsmiley,表情图需要用的库
3)httpclient,纯SOCKET封装实现的HTTP请求库
4)libogg,日志库
5)modules,各个模块类封装的动态库
     此系列文章先从基础模块开始讲解,比如回调机制,HTTP封装,数据库封装,网络请求等等,再到界面的布局实现。
     首先介绍基础模块:回调机制,回调机制的原理是用观察者模式来实现的。

一,Subject被观察者

     打开Modules工程中ModuleSubject.h文件,如图所示:
在这里插入图片描述
     ModuleSubject是一个subject类,是一个被观察者,它有两个成员变量,一个是观察者数组,一个是锁。再就是公有的方法,包括addObserver(添加观察者),removeObserver(移除观察者),以及通知观察者的一系列方法。
     大家肯定也注意到这个类是一个final不可继承的,这不符合常规的观察者设计模式,所以这里还用到了另外一个中间类来调用ModuleSubject中的实现,
在这里插入图片描述
     ModuleBase类中就只有一个成员变量m_pModuleSubject,就是我们上面说的被观察者类,在ModuleBase类中所有的函数都只是中转去调用ModuleSubject的实现。外部需要实现被观察者类(即观察对象),直接继承ModuleBase即可。

二,Observer观察者

     被观察者讲完,下面就是观察者,它的实现在文件ModuleObserver.h中
在这里插入图片描述
     这个文件中也有两个类,一个是ModuleObserverCtx类,也就是ModuleSubject中的成员变量;另一个是MKOEvent_Impl,它继承了两个虚接口,一个process()来处理事务逻辑,一个release()释放当前指针。这两个类肯定是实现观察者作用的,但是具体怎么实现呢?我们先看看被观察者如何将通知发送到观察者这里来的。我们再回到ModuleSubject.cpp文件,
在这里插入图片描述
     addObserver时创建了一个ModuleObserverCtx对象,并且把pObserObject(观察者)和handle(仿函数,也即是回调函数)填充到该对象中,加入数组。

三,事件的通知

     最后我们再看通知函数

void ModuleSubject::_asynNotifyObserver(IN const std::string& keyId, IN MKOEvent_Impl* pEvent)
{
	pEvent->m_keyId = keyId;
	module::getEventManager()->asynFireUIEvent(pEvent);
}

     很明显它中间又调用了另一个接口类UIEventManager,这就是一个消息窗口管理类,为什么要用到一个消息窗口呢?这是因为,本程序中实现的观察者模式是异步的,所以用了一个消息窗口来进行中转(此处需要区分,消息窗口不等于一个新线程,)。在它的startup函数里,创建窗口来接收消息。
在这里插入图片描述
     在ModuleSubject::_asynNotifyObserver中通过UIEventManager来触发,具体实现如下:

module::IMCoreErrorCode UIEventManager::asynFireUIEvent(IN const IEvent* const pEvent)
{
	assert(m_hWnd);
	assert(pEvent);
	if (0 == m_hWnd || 0 == pEvent)
		return IMCORE_ARGUMENT_ERROR;

	if (FALSE == ::PostMessage(m_hWnd, UI_EVENT_MSG, reinterpret_cast<WPARAM>(this), reinterpret_cast<WPARAM>(pEvent)))
		return IMCORE_WORK_POSTMESSAGE_ERROR;

	return IMCORE_OK;
}

     通过postmessage发送消息后,直接返回,不阻塞当前线程。然后在窗口过程函数中处理UI_EVENT_MSG消息。注意这里它把MKOEvent_Impl*参数传进去了。

LRESULT _stdcall UIEventManager::_WindowProc(HWND hWnd
											, UINT message
											, WPARAM wparam
											, LPARAM lparam)
{
	switch (message)
	{
	case UI_EVENT_MSG:
		reinterpret_cast<UIEventManager*>(wparam)->_processEvent(reinterpret_cast<IEvent*>(lparam), TRUE);
		break;
	case WM_TIMER:
		reinterpret_cast<UIEventManager*>(wparam)->_processTimer();
		break;
	default:
		break;
	}
	return ::DefWindowProc(hWnd, message, wparam, lparam);
}

     然后在_processEvent中调用类IEvent的虚函数来处理事务逻辑,因为上面传入的是MKOEvent_Impl,那么调用的就是MKOEvent_Impl::process();
在这里插入图片描述
     MKOEvent_Impl::process()中再去遍历当前所有的观察者,调用每一个观察者的回调函数来触发。至此完成从监听到触发的整个过程。
在这里插入图片描述

四,总结及业务实例分析

     总结一下整个过程:
1,不同的业务各自继承一个ModuleBase
2,在各个需要用到该业务的类中去将当前类绑定到该业务的观察者中。
3,在触发的地方调用asynNotifyObserver
     以代码中的一处实际应用来举例:当在联系人列表中双击打开一个新的会话时,主窗口需要清除掉对应联系人消息的未读数目,并开启一个新的会话窗口。
     主窗口类的构造函数中添加绑定观察者和回调类。
在这里插入图片描述
     双击列表事件中,发出通知
在这里插入图片描述
     主窗口类中响应回调消息,根据keyId过滤,处理对应的业务逻辑:打开新的对话框,清除未读消息等等。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/bajianxiaofendui/article/details/84842370