松散耦合组件之间的通信
发布-订阅模式是一种消息传递模式,在此模式下,发布者可在无需知道任何接收方(称为订阅方)的情况下发送消息。 同样,订阅方可在不了解任何发布方的情况下侦听特定消息。
.NET 中的事件可实现发布-订阅模式,如果不需要松散耦合(例如控件和包含它的页面),则这些事件是通信层在组件之间最简单的方法。 但是,发布服务器和订阅服务器的生存期通过对象引用彼此耦合,而订阅服务器类型必须引用发布服务器类型。 这可能会造成内存管理问题,尤其是在订阅静态或长期对象事件的对象生存期较短时。 如果不删除事件处理程序,则订阅服务器通过在发布服务器中引用它来保持活动状态,这将阻止或延迟订阅服务器的垃圾回收。
MVVM Toolkit Messenger 简介
MVVM Toolkit IMessenger
接口描述发布-订阅模式,允许不便按对象和类型引用进行链接的组件之间进行基于消息的通信。 这种机制允许发布方和订阅方在没有彼此直接引用的情况下进行通信,这有助于减少它们之间的依赖关系,同时允许独立开发和测试组件。
注意
MVVM Toolkit Messenger 是 CommunityToolkit.Mvvm
包的一部分。 如需了解如何将包添加到项目,请参阅 Microsoft 开发人员中心上的 MVVM Toolkit 简介。
警告
.NET MAUI 包含不再推荐使用的内置 MessagingCenter
类。 请改用 MVVM Toolkit Messenger。
IMessenger
接口支持多播发布-订阅功能。 这意味着可以有多个发布方发布单个消息,并且可能有多个订阅方正在侦听同一消息。 下图说明了这种关系:
CommunityToolkit.Mvvm
包附带 IMessenger
接口的两个实现。 WeakReferenceMessenger
使用弱引用,这可以简化消息订阅者的清理。 如果订阅者没有明确定义的生命周期,这是一个不错的选择。 StrongReferenceMessenger
使用强引用,这可以提高性能和更清楚地控制订阅的生存期。 如果你有一个具有高度受控生存期的工作流(例如,绑定到页面的 OnAppearing
和 OnDisappearing
方法的订阅),则如果有性能问题,则 StrongReferenceManager
可能是更好的选择。 这两个实现都可以通过引用 WeakReferenceMessenger.Default
或 StrongReferenceMessenger.Default
来使用默认实现。
注意
虽然 IMessenger
接口允许在松散耦合的类之间进行通信,但它并没有为这个问题提供唯一的体系结构解决方案。 例如,视图模型和视图之间的通信也可以通过绑定引擎和属性更改通知来实现。 此外,两个视图模型之间的通信也可以通过在导航期间传递数据来实现。
eShop 多平台应用使用 WeakReferenceMessenger
类在松散耦合的组件之间进行通信。 应用定义名为 AddProductMessage
的单条消息。 将商品添加到购物篮时,CatalogViewModel
类会发布 AddProductMessage
。 作为回报,CatalogView
类订阅消息并使用它来突出显示添加的产品,并以动画作为响应。
在 eShop 多平台应用中,WeakReferenceMessenger
用于在 UI 中更新以响应另一个类中发生的操作。 因此,消息从正在执行类的线程发布,订阅者在同一线程上接收消息。
提示
执行 UI 更新时封送给 UI 或主线程。 如果未在此线程上更新用户界面,可能会导致应用程序崩溃或变得不稳定。
如果需要从后台线程发送的消息来更新 UI,请通过调用 MainThread.BeginInvokeOnMainThread
方法在订阅方的 UI 线程上处理该消息。
有关 Messenger
的详细信息,请参阅 Microsoft 开发人员中心上的 Messenger。
定义消息
IMessenger
消息是提供自定义有效负载的自定义对象。 以下代码示例显示了在 eShop 多平台应用中定义的 AddProductMessage
消息:
public class AddProductMessage : ValueChangedMessage<int>
{
public AddProductMessage(int count) : base(count)
{
}
}
基类是使用 ValueChangedMessage<T>
whereT
定义的,可以是传递数据所需的任何类型。 消息发布者和订阅者可以收到特定类型的消息(例如,AddProductMessage
)。 这有助于确保双方都同意消息传递合同,并且随该合同一起提供的数据是一致的。 此外,此方法提供编译时类型安全性和重构支持。
发布消息
若要发布消息,需要使用 IMessenger.Send
方法。 通常可以通过 WeakReferenceMessenger.Default.Send
或 StrongReferenceMessenger.Default.Send
进行访问。 发送的消息可以是任何对象类型。 以下代码示例演示如何发布 AddProduct
消息:
WeakReferenceMessenger.Default.Send(new Messages.AddProductMessage(BadgeCount));
在此示例中,Send
方法指定供下游订阅者接收的 AddProductMessage
对象的新实例。 当多个不同的订阅者需要接收相同类型的消息且不接收错误消息时,可以添加额外第二个令牌参数以供使用。
Send
方法将使用“即发即弃”方法发布消息及其有效负载数据。 因此,即使没有注册的订阅方接收消息,消息也仍会发送。 在这种情况下,将忽略已发送的消息。
订阅消息
订阅方可以使用一种 IMessenger.Register<T>
重载进行注册以接收消息。 以下代码示例演示了 eShop 多平台应用如何订阅和处理 AddProductMessage
消息:
WeakReferenceMessenger.Default
.Register<CatalogView, Messages.AddProductMessage>(
this,
async (recipient, message) =>
{
await recipient.Dispatcher.DispatchAsync(
async () =>
{
await recipient.badge.ScaleTo(1.2);
await recipient.badge.ScaleTo(1.0);
});
});
在前面的示例中,Register
方法订阅 AddProductMessage
消息,并执行回调委托以响应接收消息。 此回调委托(指定为 lambda 表达式)执行更新 UI 的代码。
注意
避免在回叫委托中使用 this
以避免在委托中捕获该对象。 这可帮助提高性能。 改为使用 recipient
参数。
如果提供了有效负载数据,请不要尝试从回调委托中修改有效负载数据,因为多个线程可能同时访问接收到的数据。 在这种情况下,有效负载数据应不可变,以避免并发错误。
取消订阅消息
订阅者可以取消订阅他们不想再收到的消息。 这是通过 IMessenger.Unregister
重载之一实现的,如以下代码示例所示:
WeakReferenceMessenger.Default.Unregister<Messages.AddProductMessage>(this);
注意
在此示例中,调用 Unregister
不是完全必要的,因为 WeakReferenceMessenger
将允许对未使用的对象进行垃圾回收。 如果使用了 StrongReferenceMessenger
,建议为不再使用的任何订阅调用 Unregister
。
在此示例中,Unsubscribe
方法语法指定消息的类型参数和正在侦听消息的接收者对象。
摘要
MVVM Toolkit IMessenger
接口描述发布-订阅模式,允许不便按对象和类型引用进行链接的组件之间进行基于消息的通信。 这种机制允许发布方和订阅方在没有彼此引用的情况下进行通信,这有助于减少它们之间的依赖关系,同时允许独立开发和测试组件。