UWP开源项目 LLQNotifier 页面间通信利器(移植EventBus)
来源:互联网 发布:黑田官兵卫 知乎 编辑:程序博客网 时间:2024/06/03 13:48
前言
EventBus是一个Android版本的页面间通信库,这个库让页面间的通信变得十分容易且大幅降低了页面之间的耦合。小弟之前玩Android的时候就用得十分顺手,现在玩uwp就觉得应该在这平台也写个类似的库。
这个库原理很简单,就是把观察者模式封装成库,页面想收到某类通知就注册相关事件,在其他页面发出通知后就做响应。
LLQNotifier的使用:
//声明一种通知事件public class Event1{ public string Flag { get; set; }}//注册并监听事件public class subscriber{ public subscriber() { LLQNotifier.Default.Register(this); } //给收到通知后要回调的方法加上SubscriberCallback属性 [SubscriberCallback(typeof(Event1), NotifyPriority.Lowest, ThreadMode.Background)] public void Test() { Debug.WriteLine("->>>>>>>>>>subscriber>>Test"); }}//在某个地方发起事件通知LLQNotifier.Default.Notify(new Event1() { Flag = "flag" });
这样就可以了,事件接收者和发起事件通知的人互不可见,只要通知一声,所有注册这个事件的函数都会执行,并且可以设置执行的优先级,可以看到上面SubscriberCallback
属性有三个参数:
- 事件类型,表明只有接到这种事件的通知才会响应。
- 优先级,总共有5级,实际运用中可能会碰到注册同一种事件并需要控制执行先后顺序,这时优先级就派上用场。
- 线程模式,有三种:主线程,当前线程和后台线程,比方说这个方法里做的是UI相关的就用ThreadMode.Main,如果是计算相关就可以用ThreadMode.Current或ThreadMode.Background。
在EventBus中还有个sticky的概念,粘性,事件在发起后一段时间,本来所有注册者都已经响应过了,这时再有其他注册者进来按道理应该是收不到这个事件通知了,不过有了这个sticky的话就可以让新进来的注册者也能响应到这个事件,不过在LLQNotifier里暂时还没有实现这个功能,个人觉得实用性不是太强。
LLQNotifier里的注册通知的具体实现:
//注册事件,可以看到这个方法主要就是把注册者的响应方法和事件类型加到Dictionary里面,当然要先把注册里用Attribute标记的方法找出来并缓存public void Register(object subscriber){ if (_syncContext == null && SynchronizationContext.Current != null) _syncContext = SynchronizationContext.Current; List<Type> subscriptTypes = new List<Type>(); if (_subscriberDictWithType.ContainsKey(subscriber)) { return; } //这里把注册者里attrubute标记的方法找出来并封装在subscription里 IList<Subscription> subscriptionList = SubscriptionHandler.CreateSubscription(subscriber); foreach (var subscription in subscriptionList) { var subscriptionsOfType = _subscriptionDictByType.GetOrAdd(subscription.EventType, new List<Subscription>()); //这里用了一个锁数组,因为在注册时也有可能会在其他线程在通知事件来遍历,所以需要用锁来保证线程安全 lock (_locksForSubscription.GetOrAdd(subscription.EventType, new object())) { subscriptionsOfType.Add(subscription); subscriptionsOfType.Sort(); } if (!subscriptTypes.Contains(subscription.EventType)) { subscriptTypes.Add(subscription.EventType); } } _subscriberDictWithType.Add(subscriber, subscriptTypes);}
//接下来就是通知了public void Notify(object eventObj){ if (!_subscriptionDictByType.ContainsKey(eventObj.GetType())) return; var subscriptionsOfType = _subscriptionDictByType[eventObj.GetType()]; List<Subscription> subList; lock (_locksForSubscription.GetOrAdd(eventObj.GetType(), new object())) { subList = subscriptionsOfType.ToList(); } foreach (var subscription in subList) { if (subscription.IsSubscriberAlive && _subscriberDictWithType.ContainsKey(subscription.Subscriber)) { DispatchNotification(subscription, eventObj); } }}
//还有取消注册,适合用在Dispose时public void Unregister(object subscriber){ List<Type> types = null; if(_subscriberDictWithType.TryGetValue(subscriber, out types)) { foreach(var type in types) { RemoveSubscription(type, subscriber); } }}
垃圾回收
因为注册时会把注册的对象保存起来,强引用的话会导致对象不能被GC回收,表现在应用里就是页面只要打开一次,内存就会被占用,即使页面已经关掉,内存不回收,这就是内存泄露了。
所以在Subscription
里的_subscriber
是作为了一个WeakReference存在,这就避免了subscriber不能回收的情况。不过还有一个问题,subscriber在有些地方是作为Key存在的,WeakReference作key的话,对象被回收了Dictionary不会做改变,这样可能导致Dictionary里垃圾越来越多,Value也不能被回收掉。好在有个专门处理这个问题的集合:ConditionalWeakTable
,这个table可以在key被回收后删除这条记录,完美解决上面的问题。LLQNotifier里有两个地方用了到这个table,一个是Subscription
里对method做的缓存,另一个是_subscriberDictWithType
用来存储对象和对象包含的事件。
性能
关于性能我自己测试的结果是单个通知到响应时间小于1毫秒,10万个会在100毫秒以内,不过相信正常情况下是不会有这么多通知的,我在知乎日服和读读日报里大量用到LLQNotifier,没发现任何性能上的影响。
开源
项目开源地址:https://github.com/brookshi/LLQNotifier,欢迎Fork/Star
- UWP开源项目 LLQNotifier 页面间通信利器(移植EventBus)
- 【开源项目11】组件间通信利器EventBus
- Android各组件/控件间通信利器之EventBus
- Android EventBus 简化应用程序组件(Activity、Fragment、线程)间通信的开源项目
- Win32 C++项目移植到 Win10 UWP
- Android EventBus开源项目
- Android EventBus开源项目
- Android EventBus开源项目
- Android EventBus开源项目
- 模块间通信EventBus
- EventBus组件间通讯利器【入门篇】
- UWP项目中EnitityFramework开源框架使用说明(一)
- UWP项目中EnitityFramework开源框架使用说明(二)
- 利用第三方开源项目 EventBus 进行通信
- 开源项目之EventBus 源码分析
- Android开源项目——EventBus
- Android 开源项目EventBus详解
- Android开源通信框架之——EventBus
- 网页背景图片
- Geo redis实现附近的车辆/人
- hanoi塔
- 移动端事件基础
- dcsd
- UWP开源项目 LLQNotifier 页面间通信利器(移植EventBus)
- C++中的异常处理(三)
- LeetCode[167]Two Sum II
- 三十年河东三十年河西
- TeamViewer不小心注册成商业版试用期到期怎么办
- 使用 DialogFragment 和 BottomSheet 实现底部弹出框
- Spring 报 NoSuchBeanDefinitionException
- 技术文章 | 深入剖析:关于cache buffers chains的经典案例处理详解
- 解决 CPU topology doesn't match maximum vcpu count