QFramework框架学习(二) ------ 简易消息机制

QFramework 是一套 渐进式 的 快速开发 框架。目标是作为无框架经验的公司、独立开发者、以及 Unity3D 初学者们的 第一套框架。框架内部积累了多个项目的在各个技术方向的解决方案。学习成本低,接入成本低,重构成本低,二次开发成本低,文档内容丰富(提供使用方式以及原理、开发文档)。github:https://github.com/liangxiegame/QFramework


本来在介绍消息机制之前中间还有篇有限状态机的文章介绍,不过我再游戏设计模式分类中已经写了一篇,个人觉得还是很好理解的

地址在这:https://blog.csdn.net/dengshunhao/article/details/80808152

或者 QFramework作者也写了一篇:http://liangxiegame.com/post/4/

同时,本篇文章介绍内容地址:http://liangxiegame.com/post/5/

在此之前,首先你需要知道C# This扩展知识,已经给你总结好啦 : 

                     https://blog.csdn.net/dengshunhao/article/details/80944994


咳咳,看完基础开始正文

1.什么是消息机制?

使用消息机制的目的是解耦合

2.框架中所使用的消息机制

        涉及到三个类,分别是:Sender,Receiver,MsgDispatcher

public class Sender : MonoBehaviour, IMsgSender
	{
		void Update()
		{
                   this.SendLogicMsg("Receiver Show Sth", "发送:你好", "发送:世界");
		}
	}
public class Receiver : MonoBehaviour, IMsgReceiver
	{
		// Use this for initialization
		private void Awake()
		{
			this.RegisterLogicMsg("Receiver Show Sth", ReceiveMsg);
		}

		private void ReceiveMsg(object[] args)
		{
			foreach (var arg in args)
			{
				Log.I("接收:"+arg);
			}
		}
	}

   看代码我们可以看到,sender里面不停的发送信息 发送:你好 发送:世界 两条消息,接收端则在Awake中注册了接收方法,细心的应该注意到了两者传的第一个参数是相同的,再来看看效果:

                                          

      接收端一直在输出接收到的信息

  再回到代码里,发现sender receiver里面并没有啥实现方法,那就只能是继承的接口里面咯,看到这如果没看C# this扩展的童鞋估计会疑惑啦,借口不是只能声明方法吗?子类没实现怎么没报错呢?我们来仔细看看接口所在的类的代码

   首先是两个接口啦

	public interface IMsgReceiver
	{
	}

	public interface IMsgSender
	{
	}
再是消息分发器MsgDispatcher中消息捕捉器的定义,包括receiver对应的回调方法
		/// <summary>
		/// 消息捕捉器
		/// </summary>
		private class LogicMsgHandler
		{
			public readonly IMsgReceiver Receiver;
			public readonly Action<object[]> Callback;

			public LogicMsgHandler(IMsgReceiver receiver, Action<object[]> callback)
			{	Receiver = receiver;
				Callback = callback;
			}
		}

mMsgHandlerDict是消息名称对应的消息捕捉器,当receiver注册接收消息的时候会添加

                 /// <summary>
		/// 每个消息名字维护一组消息捕捉器。
		/// </summary>
		static readonly Dictionary<string, List<LogicMsgHandler>> mMsgHandlerDict =
			new Dictionary<string, List<LogicMsgHandler>>();

IMsgReceiver扩展方法(就是往mMsgHandlerDict中添加消息名对应的消息捕捉器):

                /// <summary>
		/// 注册消息,
		/// 注意第一个参数,使用了C# this的扩展,
		/// 所以只有实现IMsgReceiver的对象才能调用此方法
		/// </summary>
		public static void RegisterLogicMsg(this IMsgReceiver self, string msgName, Action<object[]> callback)
		{
			if (string.IsNullOrEmpty(msgName))
			{
				Log.W("RegisterMsg:" + msgName + " is Null or Empty");
				return;
			}
			if (null == callback)
			{
				Log.W("RegisterMsg:" + msgName + " callback is Null");
				return;
			}
			
			if (!mMsgHandlerDict.ContainsKey(msgName))
			{
				mMsgHandlerDict[msgName] = new List<LogicMsgHandler>();
			}
			var handlers = mMsgHandlerDict[msgName];
			
			// 防止重复注册
			foreach (var handler in handlers)
			{
				if (handler.Receiver == self && handler.Callback == callback)
				{
					Log.W("RegisterMsg:" + msgName + " ayready Register");
					return;
				}
			}
			handlers.Add(new LogicMsgHandler(self, callback));
		}


				Receiver = receiver;
				Callback = callback;
			}
		}

IMsgSender扩展(其实是调用已经注册的消息名对应的回调方法):

 /// <summary>
        /// 发送消息
        /// 注意第一个参数 表示是IMsgSender的扩展
        /// </summary>
        public static void SendLogicMsg(this IMsgSender sender, string msgName, params object[] paramList)
		{
			if (string.IsNullOrEmpty(msgName))
			{
				Log.E("SendMsg is Null or Empty");
				return;
			}
			if (!mMsgHandlerDict.ContainsKey(msgName))
			{
				Log.W("SendMsg is UnRegister");
				return;
			}

			var handlers = mMsgHandlerDict[msgName];
			var handlerCount = handlers.Count;

			// 之所以是从后向前遍历,是因为  从前向后遍历删除后索引值会不断变化
			// 参考文章,http://www.2cto.com/kf/201312/266723.html
			for (var index = handlerCount - 1; index >= 0; index--)
			{
				var handler = handlers[index];

				if (handler.Receiver != null)
				{
					Log.W("SendLogicMsg:" + msgName + " Succeed");
					handler.Callback(paramList);
				}
				else
				{
					handlers.Remove(handler);
				}
			}
		}

IMsgReceiver注销方法扩展(其实就是移除mMsgHandlerDict中的对应消息):

/// <summary>
		/// 注销消息
		/// 注意第一个参数,使用了C# this的扩展,
		/// 所以只有实现IMsgReceiver的对象才能调用此方法
		/// </summary>
		public static void UnRegisterLogicMsg(this IMsgReceiver self, string msgName, Action<object[]> callback)
		{
			if (msgName.IsNullOrEmpty() || null == callback)
			{
				return;
			}

			var handlers = mMsgHandlerDict[msgName];

			// 删除List需要从后向前遍历
			for (var index = handlers.Count - 1; index >= 0; index--)
			{
				var handler = handlers[index];
				if (handler.Receiver == self && handler.Callback == callback)
				{
					handlers.Remove(handler);
					break;
				}
			}
		}
  • 如果是MonoBehaviour注册消息之后,GameObject Destroy之前一定要注销消息,之前的解决方案是,自定义一个基类来维护该对象已经注册的消息列表,然后在基类的OnDestory时候遍历卸载。

猜你喜欢

转载自blog.csdn.net/dengshunhao/article/details/80945192