Messenger 深入

1、Messager交互结构和消息类型

衔接上篇,Messeger是信使的意思,顾名思义,他的目是用于View和ViewModel 以及 ViewModel和ViewModel 之间的消息通知和接收。

Messenger类用于应用程序的通信,接受者只能接受注册的消息类型,另外目标类型可以被指定,用Send<TMessage, TTarget>(TMessage message)实现,在这种情况下信息只能被传递如果接受者类型和目标参数类型匹配,

message可以是任何简单或者复杂的对象,你可以用特定的消息类型或者创建你自己的类型继承自他们。

交互结构如下所示:

消息类型如下表所示:

message消息对象类型 说明
MessageBase 简单的消息类,携带可选的信息关于消息发布者的
GenericMessage<T> 泛型消息
NotificationMessage 用于发送一个string类型通知给接受者
NotificationMessage<T>
和上面一样是一个,且具有泛型功能

NotificationMessage 向接受者发送一个通知,允许接受者向发送者回传消息
NotificationMessageAction<T> NotificationMessage的泛型方式
DialogMessage 发送者(通常是View)显示对话,并且传递调用者得回传结果(用于回调),接受者可以选择怎样显示对话框,可以使是标准的MessageBox也可也是自定义弹出窗口
PropertyChangedMessage<T> 用于广播一个属性的改变在发送者里,和PropertyChanged事件有完全箱体内各的目的,但是是一种弱联系方式

2、注册消息的模式

上篇给出了注册的方法,但是注册可以有很多种方式,最常见的就是命名方法调用和Lambda表达式调用的方式:

2.1、基本的命名方法注册

 1   // 使用命名方法进行注册
 2   Messenger.Default.Register<String>(this, HandleMessage);
 3
 4   //卸载当前(this)对象注册的所有MVVMLight消息
 5   this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
 6
 7
 8   private void HandleMessage(String msg)
 9   {
10     //Todo
11   }

2.2、使用 Lambda 注册

1   Messenger.Default.Register<String>(this, message => {
2                 // Todo
3
4   });
5   //卸载当前(this)对象注册的所有MVVMLight消息
6   this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);

3、跨线程访问

之前在第8篇《利刃 MVVMLight 8:DispatchHelper在多线程和调度中的使用》中,

我们有讨论过在异步线程中使用事件来执行和获取相关的执行步骤。但是如果异步线程中的某个方法需要操作主线程(UI线程的时候)的UI是不允许的。

Windows 中的控件被绑定到特定的UI线程(主线程)中,其他线程是不允许访问的,因为不具备线程安全性和规范性。所以后来MVVM Light才有了调度帮助类(DispatchHelper)来处理不用线程中的调度方案。

从这边可以衍生到异步线程下,对UI线程(主线程)的信息发送和接收。所以之前的代码 DispatchHelper 可以改装如下:

注册模块(ViewModel中):

 1  public class MessengerForDispatchViewModel:ViewModelBase
 2     {
 3         /// <summary>
 4         /// 构造函数
 5         /// </summary>
 6         public MessengerForDispatchViewModel()
 7         {
 8             InitData();
 9             DispatcherHelper.Initialize();
10
11             ///Messenger:信使
12             ///Recipient:收件人
13             Messenger.Default.Register<TopUserInfo>(this, "UserMessenger", FeedBack);
14         }
15 }

发送模块(异步线程中代码):

 1   private void Start()
 2         {
 3             TopUserInfo ui = new TopUserInfo();
 4
 5             //ToDo:编写创建用户的DataAccess代码
 6             for (Int32 idx = 1; idx <= 9; idx++)
 7             {
 8                 Thread.Sleep(1000);
 9                 ui = new TopUserInfo() {
10                     isFinish = false,
11                     process = idx*10,
12                      userInfo =null
13                 };
14                 DispatcherHelper.CheckBeginInvokeOnUI(() =>
15                 {
16                     Messenger.Default.Send<TopUserInfo>(ui, "UserMessenger");
17                 });
18             }
19             Thread.Sleep(1000);
20             ui = new TopUserInfo()
21             {
22                 isFinish = true,
23                 process = 100,
24                 userInfo = up
25             };
26             DispatcherHelper.CheckBeginInvokeOnUI(() =>
27             {
28                 Messenger.Default.Send<TopUserInfo>(ui, "UserMessenger");
29             });
30         }

结果:

4、释放注册信息:

4.1、基于View界面内的UnRegister的释放(为当前视图页面的Unload事件 附加 释放注册信息的功能):

1 //卸载当前(this)对象注册的所有MVVMLight消息 2 this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);

4.2、基于ViewModel类中的UnRegister释放(用户在关闭使用页面的时候同事调用该方法,释放注册,这个需要开发人员在关闭视图模型的时候发起):

1  /// <summary>
2  /// 手动调用释放注册信息(该视图模型内的所有注册信息全部释放)
3  /// </summary>
4  public void ReleaseRegister()
5  {
6    Messenger.Default.Unregister(this);
7  }

5、释放注册信息和内存处理

为了避免不必要的内存泄漏, .Net框架提出了比较实用的 WeakReference(弱引用)对象。该功能允许将对象的引用进行弱存储。如果对该对象的所有引用都被释放了,则垃圾回收机便可回收该对象。

类似将所有的注册信息保存在一个弱引用的存储区域,一旦注册信息所寄宿的宿主(View或者ViewModel)被释放,引用被清空,该注册信息也会在一定时间内被释放。

下面一个表格来源于 Laurent Bugnion 对 MVVM 的说明文档:

未取消注册时的内存泄漏风险:

可见性 WPF    Silverlight Windows Phone 8 Windows 运行时
静态 无风险 无风险 无风险 无风险
公共 无风险 无风险 无风险 无风险
内部 无风险 风险 风险 无风险
专用 无风险 风险 风险 无风险
匿名Lambda 无风险 风险 风险 无风险

6、专有信道和广播信道

6.1 过滤Messenger发送端(通过判断发送端来确认是否是发送给自己的):

 1  public class ForSourceSenderViewModel:ViewModelBase
 2     {
 3         public ForSourceSenderViewModel(){}
 4
 5         #region 全局命令
 6         private RelayCommand sendMsg;
 7         /// <summary>
 8         /// 发送消息
 9         /// </summary>
10         public RelayCommand SendMsg
11         {
12             get
13             {
14                 if (sendMsg == null) sendMsg = new RelayCommand(() => ExcuteSendMsh());
15                 return sendMsg;
16             }
17
18             set
19             {
20                 sendMsg = value;
21             }
22         }
23
24         #endregion
25
26         #region 附属方法
27         private void ExcuteSendMsh()
28         {
29             NotificationMessage nm = new NotificationMessage(this,"发送源消息");
30             Messenger.Default.Send<NotificationMessage>(nm);
31         }
32         #endregion
33
34     }
1             Messenger.Default.Register<NotificationMessage>(this, message =>
2             {
3                 if (message.Sender is ForSourceSenderViewModel)
4                 {
5                     // 判断来源来接受消息
6                     MsgInfo = message.Notification;
7                 }
8             });          

6.2 开设专用的Messenger通道:

 1   private Messenger myMessenger;
 2         public MessengerForSourceViewModel()
 3         {
 4             //构造函数
 5             myMessenger = new Messenger();
 6             SimpleIoc.Default.Register(() => myMessenger, "MyMessenger"); //注入一个Key为MyMessenger的Messenger对象
 7
 8             myMessenger.Register<NotificationMessage>(this, message =>  //注册myMessenger,开启监听
 9             {
10                 // 判断来源来接受消息
11                 MsgInfo = message.Notification;
12             });
13         } 
 1    #region 全局命令
 2         private RelayCommand sendMsg;
 3         /// <summary>
 4         /// 发送消息
 5         /// </summary>
 6         public RelayCommand SendMsg
 7         {
 8             get
 9             {
10                 if (sendMsg == null) sendMsg = new RelayCommand(() => ExcuteSendMsh());
11                 return sendMsg;
12             }
13             set
14             {
15                 sendMsg = value;
16             }
17         }
18                 #endregion
19
20         #region 附属方法
21         private void ExcuteSendMsh()
22         {
23             NotificationMessage nm = new NotificationMessage(this,String.Format("发送消息:{0}",DateTime.Now));
24             Messenger myMessenger = SimpleIoc.Default.GetInstance<Messenger>("MyMessenger");//获取已存在的Messenger实例
25             myMessenger.Send<NotificationMessage>(nm);//消息发送
26         }
27         #endregion

6.3 使用令牌(Token)区分和使用信道:这是最常用的方式。使用专属Token,可以区分不同的信道,并提高复用性。

Messenger中包含一个token参数,发送方和注册方使用同一个token,便可保证数据在该专有信道中流通,所以令牌是筛选和隔离消息的最好办法。

1  //以ViewAlert位Tokon标志,进行消息发送
2  Messenger.Default.Send<String>("ViewModel通知View弹出消息框", "ViewAlert");
 1         public MessagerForView()
 2         {
 3             InitializeComponent();
 4
 5             //消息标志token:ViewAlert,用于标识只阅读某个或者某些Sender发送的消息,并执行相应的处理,所以Sender那边的token要保持一致
 6             //执行方法Action:ShowReceiveInfo,用来执行接收到消息后的后续工作,注意这边是支持泛型能力的,所以传递参数很方便。
 7             Messenger.Default.Register<String>(this, "ViewAlert", ShowReceiveInfo);
 8             this.DataContext = new MessengerRegisterForVViewModel();
 9             //卸载当前(this)对象注册的所有MVVMLight消息
10             this.Unloaded += (sender, e) => Messenger.Default.Unregister(this);
11         }
12
13         /// <summary>
14         /// 接收到消息后的后续工作:根据返回来的信息弹出消息框
15         /// </summary>
16         /// <param name="msg"></param>
17         private void ShowReceiveInfo(String msg)
18         {
19             MessageBox.Show(msg);
20         }

7、使用内置消息

比如我们上面用到的 NotificationMessage<T> ,以及PropertyChanged-Message<T>。

Notification是一种消息通知机制;而PropertyChanged-Message主要指的是当属性改变的时候,执行通知操作。

 1   public class PropertyChangedViewModel:ViewModelBase
 2     {
 3         public const string PropertyName = "UserName"; //注册为该属性,该属性变化时进行消息发送
 4         public PropertyChangedViewModel() { }
 5
 6         #region 全局变量
 7         private String userName;
 8         /// <summary>
 9         /// 用户名称
10         /// </summary>
11         public string UserName
12         {
13             get
14             {
15                 return userName;
16             }
17
18             set
19             {
20                 String oldValue = userName;
21                 userName = value;
22                 RaisePropertyChanged(()=>UserName,oldValue,value,true);//这边相应配置上发送参数
23             }
24         }
25         #endregion
1             Messenger.Default.Register<PropertyChangedMessage<String>>(this, message =>
2             {
3                 if (message.PropertyName == PropertyChangedViewModel.PropertyName) //接受特定属性值相关信道的消息
4                 {
5                     PropertyChangedInfo = (message.OldValue + " --> " + message.NewValue);//输出旧值到新值的内容
6                 }
7             });

结果:

时间: 2024-11-03 21:17:14

Messenger 深入的相关文章

【转】Unity3D研究院之通过C#使用Advanced CSharp Messenger(五十)

http://www.xuanyusong.com/archives/2165 Advanced CSharp Messenger 属于C#事件的一种. 维基百科中由详细的说明http://wiki.unity3d.com/index.php?title=Advanced_CSharp_Messenger 上周的一天刚巧有朋友问到我这一块的知识,那么我研究出来将它贴在博客中,帮助了他也帮助我自己!哇咔咔. Advanced CSharp Messenger的特点可以将游戏对象做为参数发送.到底A

Message的进程间通信 Messenger完全解析(what)

说到Android进程间通信,大家肯定能想到的是编写aidl文件,然后通过aapt生成的类方便的完成服务端,以及客户端代码的编写.如果你对这个过程不熟悉,可以查看Android aidl Binder框架浅析: 当然今天要说的通信方式肯定不是通过编写aidl文件的方式,那么有请今天的主角:Messenger.ok,这是什么样的一个类呢?我们看下注释 This allows for the implementation of message-based communication across p

通讯聚合类Chrome 扩展:All-in-One Messenger,把主流聊天服务聚合在一个应用窗口内

原文地址:http://whosmall.com/?post=420 本文标签: Chrome扩展 Chrome浏览器 通讯聚合类Chrome扩展 All-in-One-Messenger 主流聊天服务聚合 我发现了 All-in-One Messenger,它是一款轻量的 Chrome 扩展,作用与 Franz基本相同,只需要打开一个窗口,所有的通讯服务会以网页标签栏的形式呈现,方便你自如切换,可以大大提高了沟通的效率.但是和「庞大」的 Franz 相比,All-in-One Messenge

飞书信Messenger平台的政策和审查时间

在Messenger中,我们致力于使开发者,企业和他们所服务的人民的高品质体验.今天,我们推出了Messenger平台的新政策,以及更快的审查过程.这些更新的政策会提供的平台上更明确的参与模式,包括企业邮件和标准,为应对在Messenger中订阅(了解更多基于时间的标准在这里). 本次更新,我们已经打开了标准消息的信使平台.我们将继续测试订阅短信,全面开放这一功能之前,更多地了解人们的订阅偏好.这一政策更新还将创建应用程序提交一个更精简的审查过程中,有五天或更少的复习时间. 我们将继续把人在他们

messenger跨进程通信出现ClassNotFoundException...

利用messenger实现remote进程的service和主进程通信的时候报错com.xxx.xxx.xxx.bean.xxxbean on path: DexPathList[[directory .],nativeLibraryDirectories=[/vendor/lib, /data/cust/lib, /system/lib]] 看log发现错误处是在client解析从service发过来的message中的bundle对象, 网上搜了有人说是因为类加载器的问题,然后自己在   d

Android进阶笔记04:Android进程间通讯之Messenger ( 区别于AIDL)

一. Android进程间通讯之Messenger 的引入 (1)引言:      平时一说进程间通讯,大家都会想到AIDL,其实messenger和AIDL作用一样,都可以进行进程间通讯.它是基于消息的进程间通信,就像子线程和UI线程发送消息那样,是不是很简单,还不用去写AIDL文件,是不是有点小爽.哈哈.此外,还支持记录客户端对象的Messenger,然后可以实现一对多的通信:甚至作为一个转接处,任意两个进程都能通过服务端进行通信. (2) Messenger 与 AIDL 比较:    

bootstrap大气alert弹窗组件:messenger

今天用到了这个alert控件,特意分享一下.地址是: http://www.bootcss.com/p/messenger/ 使用方法里面写的很清楚,先把js和css两个文件引入,剩下一个css文件是主题四选一.上图主题为future,个人认为最好看的一个. 引入文件后初始化该组件: $._messengerDefaults = { extraClasses: 'messenger-fixed messenger-theme-future messenger-on-top' }; 但要注意一点:

比微信落后了 Facebook Messenger上线移动支付

Facebook在周一为旗下的独立聊天应用Messenger增加移动支付功能.用户可以直接在app内完成支付,而不必再跳转到第三方网站. 随着移动支付浪潮的来临,这一动作显示了Facebook要将其13亿用户继续留在自己的软件生态系统内的决心.除了此次新增移动支付功能,此前Messenger就已经做出让人欣喜的改进,比如从前不久开始,用户可以在程序内预约Uber网车. "打造一个软件生态系统并非易事."Facebook Messenger的主管大卫·马库斯(David Marcus)说

Facebook Messenger 飞书信私密对话功能

如何发起私密对话? Messenger 中的私密对话经过端到端加密,这就意味着对话中所发送的消息仅对话双方可见,而不会为其他任何人所知,包括我们.需要注意的是,消息的接收方可以选择将对话内容与他人分享(比如:通过截图).在私密对话中,您与对方都拥有设备密钥,可用于验证对话消息是否进行了端到端加密.您还可以设置计时器,使对话消息消失.了解您可以在私密对话中发送什么内容. 要发起私密对话: 轻触  选项卡中的 轻触右上方的私密 选择消息的接收人 如有需要,可以轻触文本框中的 ,设置计时器,让消息在设

Android使用Messenger实现进程间双向通信

在了解本文即将学到的技能外,有些知识还是有必要提前知道的,这样才会更容易理解本文即将讲到的知识点.需要提前预热的知识点: 1.Android四大组件之一Service,要知道怎样去写一个Service,Service有哪两种启动方式: 2.Android Service启动方式之Bound Service: 3.Android基础知识之Messenger: 掌握了这三个知识点,就可以快速入手使用Messenger进行进程间通信了. 一.知识点回顾之Service 我们来看看官方文档关于对Serv