MvvmCross:在ViewModel中提高事件的最佳方法是什么?

6

我目前有一个视图(Android Fragment)和相应的ViewModel。现在我想在ViewModel中引发一个事件,以便View可以订阅它。

最好的方法是什么?我听说常规C#事件(委托)可能会导致内存泄漏?这就是WeakSubscribe函数的原因吗?如何将其绑定到事件?


当处理此事件时,它会执行什么操作?属性更改通知是否可行? - JimBobBennett
ViewModel 管理一个项目列表(这些项目是子 ViewModel),一旦用户点击其中一个子项目,ViewModel 应该通知视图该子项目已被点击。我还需要知道哪个项目被点击了的信息。 - Ranga B.
你在视图中如何处理这些信息? - JimBobBennett
我正在展示一个Toast来通知用户已成功将该项添加到收藏列表中。 - Ranga B.
1
我个人不会在视图中添加事件并处理它 - 相反,我会创建一个通过接口公开的通知服务,并在视图模型中调用此服务,在 Android 代码中有一个实现来显示 Toast。这样更容易进行单元测试,并且更容易实现跨平台 - 只需编写一个 iOS 实现服务的方式,以您想要的任何方式显示通知即可。 - JimBobBennett
1
我同意这种方式适用于许多问题。但是,如果您想显示 Snackbar(需要 View 作为参数)或者想在视图中触发其他操作(例如更改文本的颜色),那么这种方式就不起作用了。在这种情况下,“通知服务”是否太过受限或不足以胜任?目前我仍然使用 Toast,但我计划很快将其替换为 Snackbar。 - Ranga B.
2个回答

12

为了防止视图内存泄漏,您订阅的每个事件都需要取消订阅,可以使用WeakSubscribe或像往常一样订阅事件。

一个常见的场景是在以下情况下进行订阅:

  • Android OnResume()
  • iOS ViewWillAppear()

然后在以下情况下取消订阅:

  • Android OnPause()
  • iOS ViewWillDisappear()

WeakSubscribe

如果您想要“监听”ViewModel属性更改,则可以使用WeakSubscribe

private IDisposable _selectedItemToken;

_selectedItemToken = ViewModel.WeakSubscribe(() => 
    ViewModel.SelectedItem, (sender, eventArgs) => {
        // do something
});

请注意,WeakSubscribe() 返回一个MvxWeakEventSubscription,它也是一个IDisposable。您需要在视图中保存该订阅的引用,并在不再需要时进行处理。 保留该引用有两个原因:

  1. 稍后可以处理它
  2. 如果不保留它,您的lambda事件处理程序可能无法始终正常工作

稍后...

_selectedItemToken?.Dispose();

普通事件订阅

如果您只需要订阅ViewModel中的其他类型事件(而不是属性更改),则实际上不需要使用WeakSubscribe。您可以像处理任何对象一样,向ViewModel添加事件监听器。

ViewModel.AnEvent += YourDelegate;

后来...

ViewModel.AnEvent -= YourDelegate;

不要忘记最后一步。这将防止内存泄漏。就像我说的,Android的OnPause()和iOS的ViewWillDisappear()是做这件事情的好地方。

这样,当视图被释放时,你的ViewModel就不会停留在内存中,因此你的视图可以正确地被垃圾回收。


看起来很有前途。你介意给我一个简单的例子,说明如何使用自定义委托类型订阅事件吗?我在这方面遇到了各种各样的问题。 - Ranga B.
你已经尝试了什么?你遇到了什么问题? - xleon
我明白了。我完全误解了你的问题。我会相应地进行更新。 - xleon
谢谢。只是最后一个问题来澄清一下:SelectedItem将是一个常规的公共属性,而不是事件?所以不支持事件,我总是要通过属性和NotifyPropertChanged机制去实现吗? - Ranga B.
太棒了!谢谢。 - Ranga B.
显示剩余2条评论

1
如果您在临时对象中订阅事件并在释放临时对象之前不取消订阅,则可能会创建泄漏。在您的情况下,这种可能性很小,因为视图模型很可能只会被创建一次。
由于您正在使用mvvm,事件的替代方案是Messengers,您可以在流行的mvvm框架(如MvvmLight或MvvmCross)中找到其实现。这为您提供了真正分离的事件,因为您只需要知道消息的格式,而不需要知道发送者的任何信息(在您的情况下是ViewModel)。使用Messenger,您只需订阅消息类型,而发送者可以在应用程序中的任何位置。

2
没有必要将视图与ViewModel分离。这也适用于反过来(ViewModel不应该知道视图)。为什么要使用信使,当你可以直接订阅ViewModel的变化呢? - xleon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接