Using the FM Messaging System for In-process Publish and Subscribe on Windows, OSX and iOS.

For quite awhile I’ve been using a messaging bus within my apps to de-couple different sub-systems from each other. I use this heavily in my MVVM-based apps to minimise the dependencies between my Views and ViewModels, but it applies to non-MVVM apps of reasonable complexity as well.

For example, I have one app that is collecting samples from an external sensor and saving them in a collection in my model. When that happens, it could notify directly all the other classes, components and forms in my app that need to be updated when the samples change. However, that would build a lot of dependencies between my sample storage code and the rest of my app, making it both harder to test and harder to extend.

Instead, when my class updates the sample collection, it simply publishes a SampleAdded message. Any other classes, components or forms that are interested in knowing when a new sample is added can simply subscribe to the same SampleAdded message. Apart from a common dependency on the messaging system (which can always be managed through some form of IoC), everything is kept nice and separate.

If you’ve seen the term “Enterprise Service Bus” around, this is, at a high-level, much the same thing but in my case it’s purely in-process, not across different processes on different machines.

I’ve been doing this in VCL apps with my own, simple messaging system for awhile, but I always hated having my own ie. that I had to write it and maintain it. I’d rather use something already in the box if I could get away with it. Vincent published a very nice implementation some time ago that leverages the windows messaging support built into the VCL, but by that stage I’d already built my own, and if I was going to change, I was going to change to something cross-platform.

Recently I really did need a cross platform version, and rather than port my old one, Vincent’s elegant hack prompted me investigate how the FM framework does its messaging. Turns out that tucked away in FMX.Messages is exactly what I wanted, in the form of TMessageManager and TMessage.

I’ve built a little sample app that shows the process of defining, subscribing to, and publishing, custom messages.

Typing a string into the TEdit at the top of the screen and pressing the Send button publishes a custom TMessage descendant I’ve defined called TTextMessage, using the following code:

MessageBus.SendMessage(self, TTextMessage.Create(Edit1.Text));

MessageBus is just a TMessageManager reference I’ve instantiated by calling TMessageManager.Create

TTextMessage is defined as:

TTextMessage = class(TMessage)
private
  FText: string;
public
  property Text : string read FText;
  constructor Create(const Text : string); virtual;
end;

I also have to register my custom message with the messaging system with the following code in my FormCreate event:

MessageBus.RegisterMessageClass(TTextMessage);

That’s it. I’m now publishing messages whenever the Send button is pressed with no knowledge of which other parts of my app are subscribing to those messages,

But hang on, nobody is currently subscribing! Doesn’t matter. My publisher doesn’t know if there are zero, one or one thousand subscribers. Makes no difference to it.

However, it’s not much of an app if nobody subscribes to the message, so how does this subscription business work? Let’s look at the Groupbox on the bottom left. When the Subscribe button is pressed, it calls TMessageManager.SubscribeToMessage, passing in both the type of message to subscribe to and also a Listener method. The Listener method is an anonymous method that should be called when messages of the matching type are published.

var
  TextMessageListener : TMessageListener;
begin
  TextMessageListener := procedure(const Sender : TObject; const M : TMessage)
                         begin
                           ListBox1.Items.Add('TTextMessage Received. Text = ' + (M as TTextMessage).Text);
                         end;

  fFirstSubscriberID := MessageBus.SubscribeToMessage(TTextMessage, TextMessageListener);

In this case, my MessageListener is simply adding some text to the Listbox when a message is published. Note also that SubscribeToMessage returns a SubscriberID which you should hang on to if you want to be able to Unsubscribe from this message, which is what I do when the Unsubscribe button is pressed, like this:

MessageBus.Unsubscribe(TTextMessage, fFirstSubscriberID);

The Subscribe and Unsubscribe buttons in the groupbox on the bottom right do much the same thing, so you can muck about with publishing to zero, one or two subscribers and see the results.

It’s also worth realising that a subscriber to one message can also be a publisher of other messages. The groupbox in the bottom left that we’ve just looked at, which subscribes to the TTextMessage and adds a string to the Listbox, also publishes a different sort of message, a TListboxSelectionChangedMessage when, surprise, surprise, the listbox selection is changed. In turn, the Groupbox in the bottom right, in addition to subscribing to TTextMessage, also subscribes to TListboxSelectionChangedMessage and adds an appropriate string to its listbox.

I should note that publishing a message is a synchronous process. Your call to SendMessage will not return until each subscriber’s MessageListener code is run in turn. So I guess your publisher does know the difference between zero subscribers and a thousand, as it’ll take longer for the SendMessage call to return.

At this point there are possibly a few questions in your head:

1) Great, now I can take a simple app and over-complicate it with Messages! Well, my answer to that is if you’re writing a simple app, then you definitely don’t want to do this. Write it the normal old way and go home early, However, if you’ve ever worked on apps of even medium complexity and struggled with out of control dependencies, you can probably see that this is one way to break some of those ties. It also fits really nicely if you’ve got an app with a plugin system, allowing plugins to subscribe to messages (and even publish them) without the main app knowing about it.

2) These kinda look a bit like events. Yeah, in the right light they do, which is exactly why I used the selection changed scenario for the second one. As Delphi developers we’re already used to thinking in terms of event-driven systems. This just takes it further, allowing for multiple receivers and receivers outside the same form more easily.

Nothing comes for free of course:

1) There is overhead with all this message passing. I haven’t bothered measuring it, as in the scenarios I’ve been using it it’s never been an issue. But it’s there. If it matters to you, measure it and decide if you can live with it or not.

2) Also, one person’s “easier to read” code is another person’s obfuscated mess. I sometimes love that my code is cleanly separated and the publishers know nothing of the subscribers. Other times I wish I just had all the code laid out in one method, so I can see exactly what is going on. But let’s be honest, we’ve been on this slippery slope since we moved away from procedural code, and everyone has their own place on the slope they are comfortable,

All the sample code is available here.

4 Comments

  • Does the debugger let you run through the subscribers? I’m guessing from the synchronous nature that it is, but message passing often breaks that. Are there any multiplatform threading libraries yet – OTL doesn’t mention it AFAIK.

    • @mozmozmoz Yep. If you enable debug dcu’s you can step through the messagemanager as well. 
       
      re: threading libraries, nothing of the scale of OTL. There are a couple that make working with threads easier, like David Clegg’s tanonymousthread, but nothing like the constructs in OTL AFAIK. Not sure if Primoz is looking at porting OTL, but I hope so.

  • I’ve done similar things using custom Windows Messages, but it’s great to see that FireMonkey has the facilities to do it cross-platform. Thank you for the great, simple explanations and the humorous pondering at the end!

  • I got very excited when I found this post until I got to the bit about it being synchronous. I assume that means its not thread safe either.
    I have written my own thead safe messaging system based on TThread.Queue and anonymous methods. I would love for my unit to be critiqued by by other developers. Can I send it to you?

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>