Skip to content

cocos2d x 3.3 012 事件分发机制

cheyiliu edited this page Jan 30, 2015 · 18 revisions

相关类

  • 什么是事件? Event及子类EventXXX,核心成员_type
  • 谁监听事件? EventListener及子类EventListenerXXX,核心成员_onEvent
  • 谁派发事件? EventDispatcher分发器,核心方法dispatchEvent,(导演类全局实例化了一个EventDispatcher)
  • 谁产生事件? 对接的各平台和cocos框架以及自定义。在目录cocos2d-x-3.3/cocos/下, 执行命令ack dispatchEvent便知
  • 事件优先级(后面源码分析将验证)
    • 总原则
      • A lower priority will be called before the ones that have a higher value.
      • 对于SceneGraphPriority(优先级0),高的 Z Order 的节点优先于低的 Z Order 节点,这样确保前面的元素获取触控事件。
    • 优先级0: The priority of scene graph will be fixed value 0
    • 优先级1: Custom event will use a fixed priority of 1.
    • 优先级N: 由addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);指定,但不能指定为0。

类图

cocos 3.3 event

注册和回调

//创建监听器
    _mouseListener = EventListenerMouse::create();
// 注释:在create的调用中, 会调到基类init方法, 在init方法中
// 会给基类核心成员_onEvent赋值,并将_onEvent和成员
// onMouseMove onMouseUp onMouseDown onMouseScroll
// 之间建立调用关系。
// 相当于提供一层抽象, EventDispatcher只关系EventType,然后根据EventType
// 回调_onEvent, 事件的进一步划分就在_onEvent作处理了。

//监听器的回调赋值
    _mouseListener->onMouseMove = [=](Event *event){...};
    _mouseListener->onMouseUp = [=](Event *event){...};
    _mouseListener->onMouseDown = [=](Event *event){...};
    _mouseListener->onMouseScroll = [=](Event *event){...};

//加入监听
    _eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);




//事件派发
    Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);

//回调,在dispatchEvent里最终回调到listener->_onEvent(event);
  • 回调过程概括为:
Director::getInstance()->getEventDispatcher()->dispatchEvent(&event);
listener->_onEvent(event);
_onEvent里面在调用更细分的事件函数,eg. onMouseMove

有一处例外就是, EventListenerTouch的_onEvent在init的时候传入的是nullptr, 故在dispatchEvent的时候, 有个专门的函数dispatchTouchEvent用来处理touch事件的回调。

注册和回调相关源码

addEventListener

EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName,
		const std::function<void(EventCustom*)>& callback)
{
	EventListenerCustom *listener = EventListenerCustom::create(eventName, callback);
//优先级1,自定义事件
	addEventListenerWithFixedPriority(listener, 1);

	return listener;
}

void EventDispatcher::addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority)
{
	CCASSERT(listener, "Invalid parameters.");
	CCASSERT(!listener->isRegistered(), "The listener has been registered.");
//优先级0被系统暂用,和scene graph相关
	CCASSERT(fixedPriority != 0, "0 priority is forbidden for fixed priority since it's used for scene graph based priority.");

//必要的检查,看具体实现,主要判断是否设置回调函数
	if (!listener->checkAvailable())
	return;

	listener->setAssociatedNode(nullptr);
	listener->setFixedPriority(fixedPriority);
	listener->setRegistered(true);
	listener->setPaused(false);
//稍候看这个函数
	addEventListener(listener);
}

void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
	CCASSERT(listener && node, "Invalid parameters.");
//防止重复注册
	CCASSERT(!listener->isRegistered(), "The listener has been registered.");
//必要的检查,看具体实现,主要判断是否设置回调函数
	if (!listener->checkAvailable())
	return;

	listener->setAssociatedNode(node);
	listener->setFixedPriority(0);//优先级0
	listener->setRegistered(true);
//稍候看这个函数
	addEventListener(listener);
}

//上面几个api都调用到这个protected的函数了
void EventDispatcher::addEventListener(EventListener* listener)
{
//_inDispatch的取值含义,它代表有几个事件正在被派发
//0代表EventDispatcher正闲着
//n代表EventDispatcher正在派发n个事件
//(相关代码见dispatchEvent方法里的栈变量DispatchGuard guard(_inDispatch);)

	if (_inDispatch == 0)//正闲着,则强制加入
	{
		forceAddEventListener(listener);
	}
	else
	{//EventDispatcher忙呢,先放在_toAddedListeners
		_toAddedListeners.push_back(listener);
	}

	listener->retain();
}




void EventDispatcher::forceAddEventListener(EventListener* listener)
{
	EventListenerVector* listeners = nullptr;
	EventListener::ListenerID listenerID = listener->getListenerID();
	//_listenerMap是用于存放所用的监听器的
	//map的key是监听器id,value是一个容器。
	//每一类监听器的id一样,是个字符串,监听器的静态成员
	//先找id,也就是先找有这类监听器注册过没
	auto itr = _listenerMap.find(listenerID);
	if (itr == _listenerMap.end())
	{//没注册过,在map里插入一项(不是插入的这个监听器,注意_listenerMap的结构)

		listeners = new (std::nothrow) EventListenerVector();
		_listenerMap.insert(std::make_pair(listenerID, listeners));
	}
	else
	{//注册过,取得这类型的监听器容器,本质是个vector
		listeners = itr->second;
	}

	//往这个类型的监听器容器里放入这个监听器
	listeners->push_back(listener);

	if (listener->getFixedPriority() == 0)
	{//优先级为0的处理,先setDirty,更新_priorityDirtyFlagMap
		setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);

		auto node = listener->getAssociatedNode();
		CCASSERT(node != nullptr, "Invalid scene graph priority!");

		//再将这个listener放入到另外一个成员中(_nodeListenersMap)
		//作用我猜测是作为一个辅助数据,空间换时间
		associateNodeAndEventListener(node, listener);

		if (node->isRunning())
		{
			//resume
			resumeEventListenersForTarget(node);
		}
	}
	else
	{
		setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
	}
}

  • 小结下addEventListener,public的API都调用到addEventListener,将监听器存储到map,针对SceneGraphPriority有额外的辅助数据变量。并设置dirty。
  • 留个TODO,setDirty干嘛的?新加入监听器后,设置dirty标志,在派发事件时会先对对应类型监听器按优先级排序,见_priorityDirtyFlagMap和sortEventListeners, dispatchEvent

removeEventListener


void EventDispatcher::removeEventListener(EventListener* listener)
{
	if (listener == nullptr)
	return;

	bool isFound = false;

	//定义一个回调对象,作用是断开和node的联系,并从容器中清除
	auto removeListenerInVector = [&](std::vector<EventListener*>* listeners) {
		if (listeners == nullptr)
		return;

		for (auto iter = listeners->begin(); iter != listeners->end(); ++iter)
		{
			auto l = *iter;
			if (l == listener)
			{
				CC_SAFE_RETAIN(l);
				l->setRegistered(false);
				if (l->getAssociatedNode() != nullptr)
				{
					//断开和node的联系
					dissociateNodeAndEventListener(l->getAssociatedNode(), l);
					l->setAssociatedNode(nullptr); // nullptr out the node pointer so we don't have any dangling pointers to destroyed nodes.
				}

				if (_inDispatch == 0)
				{
					listeners->erase(iter);//从容器中清除
					CC_SAFE_RELEASE(l);//不一定和CC_SAFE_RETAIN配对呢? 后面代码还有一次release
				}

				isFound = true;
				break;
			}
		}
	};

	//从map的结构中找吧
	for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();)
	{
		auto listeners = iter->second;
		auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
		auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();

		removeListenerInVector(sceneGraphPriorityListeners);
		if (isFound)
		{
			// fixed #4160: Dirty flag need to be updated after listeners were removed.
			setDirty(listener->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY);
		}
		else
		{
			removeListenerInVector(fixedPriorityListeners);
			if (isFound)
			{
				setDirty(listener->getListenerID(), DirtyFlag::FIXED_PRIORITY);
			}
		}

#if CC_NODE_DEBUG_VERIFY_EVENT_LISTENERS
		CCASSERT(_inDispatch != 0 ||
				!sceneGraphPriorityListeners ||
				std::count(sceneGraphPriorityListeners->begin(), sceneGraphPriorityListeners->end(), listener) == 0,
				"Listener should be in no lists after this is done if we're not currently in dispatch mode.");

		CCASSERT(_inDispatch != 0 ||
				!fixedPriorityListeners ||
				std::count(fixedPriorityListeners->begin(), fixedPriorityListeners->end(), listener) == 0,
				"Listener should be in no lists after this is done if we're not currently in dispatch mode.");
#endif

		if (iter->second->empty())
		{
			_priorityDirtyFlagMap.erase(listener->getListenerID());
			auto list = iter->second;
			iter = _listenerMap.erase(iter);
			CC_SAFE_DELETE(list);
		}
		else
		{
			++iter;
		}

		if (isFound)
		break;
	}

	if (isFound)
	{
		// 确保前面CC_SAFE_RETAIN
		CC_SAFE_RELEASE(listener);
	}
	else
	{
		//从待添加的容器中删除
		for(auto iter = _toAddedListeners.begin(); iter != _toAddedListeners.end(); ++iter)
		{
			if (*iter == listener)
			{
				listener->setRegistered(false);
				listener->release();
				_toAddedListeners.erase(iter);
				break;
			}
		}
	}
}


//其他remove函数也类似,略过

dispatchEvent


void EventDispatcher::dispatchEvent(Event* event)
{
	if (!_isEnabled)
	return;

	//排序SceneGraph前的准备
	updateDirtyFlagForSceneGraph();

	//_inDispatch++
	DispatchGuard guard(_inDispatch);

	//对touch的特殊处理,因为touch的监听器的_onEvent为空
	if (event->getType() == Event::Type::TOUCH)
	{
		dispatchTouchEvent(static_cast<EventTouch*>(event));
		return;
	}

	auto listenerID = __getListenerID(event);

	//按优先级排序
	sortEventListeners(listenerID);

	auto iter = _listenerMap.find(listenerID);
	if (iter != _listenerMap.end())
	{
		//找到了这个类型的监听器容器
		auto listeners = iter->second;

		//回调
		auto onEvent = [&event](EventListener* listener) -> bool {
			event->setCurrentTarget(listener->getAssociatedNode());
			//真正调到监听器
			listener->_onEvent(event);
			return event->isStopped();
		};
                //事件的优先级体现在下面的函数中
		dispatchEventToListeners(listeners, onEvent);
	}


	updateListeners(event);//对该删除的删除,改加入的加入。。。
}



void EventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
    bool shouldStopPropagation = false;
    auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
    auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
    
    ssize_t i = 0;
    // priority < 0
    if (fixedPriorityListeners)
    {
        CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");
        
        if (!fixedPriorityListeners->empty())
        {
            for (; i < listeners->getGt0Index(); ++i)
//既然监听器都排序了实现的时候是否可以直接比较是否小于0,而不用维护着这样一个index?
//getGt0Index相关的代码就可以省略了
            {
                auto l = fixedPriorityListeners->at(i);
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
    
    if (sceneGraphPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority == 0, scene graph priority
//优先级为0的顺序决定于visitTarget中的算法

            for (auto& l : *sceneGraphPriorityListeners)
            {
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }

    if (fixedPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority > 0
            ssize_t size = fixedPriorityListeners->size();
            for (; i < size; ++i)
//注意这里的i的作用范围, 同时注意对于fixedPriorityListeners来说
//priority是不会==0的,原因见add linster的代码
            {
                auto l = fixedPriorityListeners->at(i);
                
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
}

  • visitTarget, 想必和z-order排序相关了

小结

  • 事件分发本质是采用的观察者模式: 注册观察者,事件产生并回调,取消注册
  • 事件分优先级,由EventDispatcher维护管理,开篇已经提及
  • 针对setSwallowsTouches的分析,见cocos2d-x-3.3-009-核心概念和相关类-层

扩展阅读

Clone this wiki locally