Unsubscribe from Your MessagingCenter Messages

0 Comments

You've probably hit a point where you needed to use Xamarin.Forms MessagingCenter. It's an easy tool to use in the framework, however, it can cause a lot of headaches if you forget to unsubscribe from messages. If you are not familiar with MessagingCenter, take a look at the documentation here.

I have commonly seen MessagingCenter used to send a message that triggers navigation within the app. For example:

// Send a message to trigger navigation from some place
MessagingCenter.Send<string>(this, "NavigateDetail", detailItem);
// Some other place receives it and does the navigation
MessagingCenter.Subscribe<string, DetailClass>(this, "NavigateDetail", async (detail) => 
{
   await Naviation.PopAsync();
});

This might work the first time, but lets assume you subscribed in the constructor of your Page class. The next time you get this "NavigateDetail" message, you might run into a crash:

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
...
 at System.ThrowHelper.ThrowArgumentOutOfRangeException (ExceptionArgument argument, ExceptionResource resource) [0x00000] in <snipped>/throwhelper.cs:93 
  at System.ThrowHelper.ThrowArgumentOutOfRangeException () [0x00000] in <snipped>/throwhelper.cs:56 
  at System.Collections.Generic.List`1[T].get_Item (Int32 index) [0x0000c] in <snipped>/list.cs:181 
  at Xamarin.Forms.NavigationProxy.Pop () [0x00012] in <filename unknown>:0 
  at Xamarin.Forms.NavigationProxy.OnPopAsync (Boolean animated) [0x00012] in <filename unknown>:0 
  at Xamarin.Forms.NavigationProxy.PopAsync () [0x00000] in <filename unknown>:0 

Problem: A message is subscribed to whenever the Page is created and it's never unsubscribed from.

Solution: Unsubscribe from the message in the message action/handler, or use a pattern of subscribing in OnAppearing and unsubscribing in OnDisappearing.

You could solve this by doing something like this:

// Some other place receives it and does the navigation
MessagingCenter.Subscribe<string, DetailClass>(this, "NavigateDetail", async (detail) => 
{
   MessagingCenter.Unsubscribe<string>(this, "NavigateDetail");
   await Naviation.PopAsync();
});

Without this, the Page that is being popped (PopAsync) would retain in memory and still receive the message. However, it's not part of the Navigation hierarchy anymore, so Navigation.PopAsync() throws an exception. It's even easier to see the issues caused by this by logging the object:

MessagingCenter.Subscribe<string, DetailClass>(this, "NavigateDetail", async (detail) =>  
{
   System.Diagnostics.Debug.WriteLine("NavigateDetail: " + this.GetHashCode());
   await Naviation.PopAsync();
});

You'll see multiple objects logging this message, which is not what you would want to happen.

Messages in Renderers

Be careful when using MessagingCenter in renderers. When a Page is removed (PopAsync, PopModalAsync) from Navigation, the renderers are disposed of. You can override Dispose and unsubscribe from your messages there, same as above.

Troubleshooting

If you still have issues, even after unsubscribing, make sure that your <T> arguments for Unsubscribe match your message. For example, given this subscription:

MessagingCenter.Subscribe<byte[]>(this, "ImageData", (data) =>
{
   MessagingCenter.Unsubscribe<string>(this, "ImageData");
   // Do work
});

This will compile and run fine, except the message is not properly unsubscribed from because the <T> arguments don't match. The message name is the same, but both things must match. I've missed that a few times.



This weeks coffee, Alabaster's Nicaragua / Yader / Direct Trade


Mango & floral aromatics, yellow delicious apple, crisp & well balanced, pomegranate finish.

Comments