UWP开源项目 LLQNotifier 页面间通信利器(移植EventBus)
前言
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)的更多相关文章
- [转] 微信小程序页面间通信的5种方式
微信小程序页面间通的5种方式 PageModel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的. 如上图,这是一个常见结构的小程序:首页是一个 ...
- JavaScript之iframe页面间通信
[1] iframe父子页面间通信 1.相互调用对方的方法 |> 子级页面调用父级页面 window.parent.父级页面方法(args) |> 父级页面调用子级页面 document. ...
- 如何实现electron多页面间通信
如何实现electron多页面间通信 1,业务需求: 总共有两个页面,页面A显示数据,页面B处理数据,主线程Main 2,实现的技术方案: 在主线程中打开页面A和B,B页面不进行显示,主要负责处理从A ...
- 【开源项目11】组件间通信利器EventBus
概述及基本概念 **EventBus**是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间.组件与后台线程间 的通信.比如请求网络,等网络返回时通过Han ...
- js页面间通信方法(storage事件)(浏览器页面间通信方法)
在写页面的时候有时会遇到这样的需求,需要两个页面之间传递数据或者一个事件.这个时候,就需要用到我今天所要讲的storage事件,学习这个事件之前,需要先了解localStorage的用法.具体用法可以 ...
- EventBus完全解析--组件/线程间通信利器
github地址:https://github.com/greenrobot/EventBus 1, Android EventBus实战, 没听过你就out了 2, Android EventBu ...
- React Native 中 跨页面间通信解决方案之 react-native-event-bus
https://github.com/crazycodeboy/react-native-event-bus 用法: A页面和B页面中都有相同的列表,点击B页面中的收藏按钮,A页面会跟着更新 impo ...
- 从UWP到SWIFT-页面间反向传值
页面1跳转到页面2,在页面2点击button后,页面1的内容被改变.实际使用 protocol(就是c#中的interface),将页面1的viewcontroller转换为protocol传入页面2 ...
- vue项目各页面间的传值
githut地址:https://github.com/liguoyong/vueobj1 一.父子之间主键传值:(主要是在父主件里的子主件传递参数,然后再子主件里用props接收) 例如Father ...
随机推荐
- Mysql字符集知识总结
字符集&字符编码方式 字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,这里的字符可以是英文字符,汉字字符,或者其他国家语言字符. 常见字符集 ...
- Spring中@Autowired注解、@Resource注解的区别
Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource.@PostConstruct以及@PreDestroy. @Resour ...
- java 的常用设计模式--大话设计模式
设计模式:一个程序员对设计模式的理解:“不懂”为什么要把很简单的东西搞得那么复杂.后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“简单”就是一把钥匙开一把 ...
- maven依赖本地非repository中的jar包-依赖jar包放在WEB-INF/lib等目录下的情况客户端编译出错的处理
MAVEN 今天在使用maven编译打包一个web应用的时候,碰到一个问题: 项目在开发是引入了依赖jar包,放在了WEB-INF/lib目录下,并通过buildpath中将web libariary ...
- CStdioFile CString 读写中文
TCHAR* old_locale = _tcsdup( _tsetlocale(LC_CTYPE,NULL) ); _tsetlocale( LC_CTYPE, _T("chs" ...
- DOS命令:IIS安装与卸载
//IIS7完全安装 start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer;IIS-CommonHttpFeatures;IIS-StaticCont ...
- python异常
Std 异常 异常名 描述 1 Exception 所有的异常 2 StopIteration 迭代器的next()方法不指向任何对象 3 SystemExit sys.exit() 函数执行 4 S ...
- nginx参数说明
一.nginx的核心配置: >>> 正常运行的必备配置: 1. user username [groupname]; #指定运行worker子进程的用户或组 2. pid /path ...
- linux 文件系统解析及相关命令
简介 文件系统就是分区或磁盘上的所有文件的逻辑集合. 文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件.目录.软连接及文件保护信息等都存储在其中. 不同Lin ...
- STM32重映射(PinRemap)的使用,注意!
STM32重映射,内容和细节稍后补充,这里只说几个注意点,花了我一晚上的时间调试终于找到问题所在了... 芯片: STM32f107vct6 晶振: 25M 通过分频器与锁相环,使系统时钟为72M 背 ...