BMessenger

What is BMessenger?

BMessenger is a class in Haiku OS that represents a communication link between different parts of the operating system. It provides a mechanism for processes to send and receive messages asynchronously, allowing them to communicate with each other without blocking each other's execution.

It represents a target to which messages can be sent. This target can be a BHandler or a BLooper—components responsible for handling messages within an application.

The most significant (set of) method(s) in the class is SendMessage(), which sends its message to the target. For a local target SendMessage() is roughly equivalent in terms of efficiency to posting a message directly to the messenger's target (i.e. BLooper::PostMessage()).

If you supply a target BMessenger or BHandler to SendMessage() the method will return immediately after delivery and the response will be handled asynchronously, otherwise the method will return once the reply has been delivered or after a set timeout.

Key Features of BMessenger

  1. Asynchronous Messaging:BMessenger allows you to send messages to a target object asynchronously, allowing components to continue their tasks without waiting for a response. This is particularly useful for maintaining the responsiveness of  user interfaces.
  2. Loose Coupling: You can send messages to a BHandler object without needing a direct reference to the object.
  3. Thread-Safe:BMessenger is thread-safe, allowing multiple threads to send messages to the same target object simultaneously.

How Does BMessenger Work?

The key to understanding BMessenger is its ability to encapsulate a BHandler object and send messages to the target BHandler using the SendMessage() function. Here's how it works:

  1. Encapsulating a BHandler:You create a BMessenger object by passing a BHandler object to its constructor.
  2. Sending Messages:You send messages to the target BHandler using the SendMessage() function of the BMessenger object.

Real Life Example

Imagine you have a magical letter. This letter can carry all kinds of information. You can write on it things like your name, age, favorite color, and anything else you want to tell someone.

Now, imagine you have a special friend called BMessenger. This friend knows how to send your magical letter to other friends, like BHandler and BLooper. These friends can do different things with your letter, depending on what you wrote on it.

So, BMessage is like your magical letter, and BMessenger is like your friend who knows how to deliver it to the right place.

Example Usage of BMessenger

Let's say you have a BButton object and you want to send a message to a BHandler object when the button is clicked. You can achieve this using BMessenger:

#include <Messenger.h>
#include <Button.h>
#include <Message.h>
#include <Handler.h>
#include <stdio.h>

class MyHandler : public BHandler {
public:
    MyHandler() {}

    void MessageReceived(BMessage* message) {
        switch (message->what) {
            case 'BTN_CLICKED':
                printf("Button clicked!\n");
                break;
            default:
                BHandler::MessageReceived(message);
                break;
        }
    }
};

int main() {
    BButton* button = new BButton("MyButton", "Click Me",
                                  new BMessage('BTN_CLICKED'));

    MyHandler* handler = new MyHandler();
    BMessenger messenger(handler);

    button->SetTarget(messenger);

    // Add the button to a window and display the window
    // ...

    return 0;
}

Explanation:

1 Creating a Messenger:

MyHandler* handler = new MyHandler();
BMessenger messenger(handler);
  • We create a MyHandler object and encapsulate it in a BMessenger object.

2 Setting the Target:

button->SetTarget(messenger);
  • We set the BMessenger object as the target for the button.

3 Sending a Message:

  • The button sends a message to the BMessenger object when it is clicked, and the BMessenger object forwards the message to the MyHandler object.
   +------------------------+       +------------------------+
   |       BButton          |       |       MyHandler        |
   +------------------------+       +------------------------+
           |                                |
    +--------------+                   +----------------+
    |  BMessenger  |------------------>|    BHandler    |
    +--------------+    SendMessage()  +----------------+
           |                                |
    +--------------+                   +----------------+
    |  BMessage    |<------------------| BMessageQueue  |
    +--------------+                   +----------------+
  • BButton: Represents the UI component, such as a button, that triggers an action.
  • MyHandler: Represents the message handler that processes the message.
  • BMessenger: Acts as a messenger between the UI component and the message handler. It encapsulates the BHandler object (MyHandler) and forwards messages to it.
  • BMessage: Represents the message sent from the UI component to the message handler.
  • BMessageQueue: Represents the queue where messages are stored before being processed by the message handler.

OR

messenger.SendMessage(&message);

4 Handle the Message:

In the target (BHandler or BLooper), implement the MessageReceived() function to handle the incoming message.

Workflow:

  1. User Interaction:The user interacts with the UI component (e.g., clicks a button).
  2. Message Creation:The UI component creates a message (e.g., a button click message) and sends it to the BMessenger object.
  3. Message Forwarding:The BMessenger object forwards the message to the target BHandler object (MyHandler).
  4. Message Processing:The BHandler object (MyHandler) processes the message and performs the necessary actions.

Example Usage of BMessenger:

#include <Messenger.h>
#include <Handler.h>
#include <Message.h>
#include <stdio.h>

class MyHandler : public BHandler {
public:
    MyHandler() : BHandler() {}

    virtual void MessageReceived(BMessage* message) {
        // Handle the message here
        printf("Message received!\n");
    }
};

int main() {
    MyHandler handler;
    BMessenger messenger(&handler);

    // Create a message
    BMessage message;
    // Add data to the message...
    
    // Send the message to the handler
    messenger.SendMessage(&message);

    return 0;
}

In this example, we create a BMessenger to communicate with a MyHandler object. Then, we create a message and send it using the messenger. When the message is sent, the MessageReceived() function in MyHandler is called to handle the message.

BMessenger Functions

1 Constructor:

BMessenger();

BMessenger(const char* signature,
	team_id team = -1,
	status_t* result = NULL);

BMessenger(const BHandler* handler,
	const BLooper* looper = NULL,
	status_t* result = NULL);

Constructor with Signature and Team ID:

BMessenger(const char* signature, team_id team = -1, status_t* result = NULL):

  • Creates a messenger to communicate with a target application.
    • signature: The signature of the target application.
    • team: The team ID of the target application.
    • result: Pointer to a status_t variable to receive the result of the operation.

Constructor with Handler and Looper:

BMessenger(const BHandler* handler, const BLooper* looper = NULL, status_t* result = NULL):

  • Creates a messenger to communicate with a specific BHandler within a BLooper.
    • handler: Pointer to the target BHandler.
    • looper: Pointer to the BLooper that contains the target BHandler.
    • result: Pointer to a status_t variable to receive the result of the operation.

2 Send Message

status_t				SendMessage(uint32 command,
										BHandler* replyTo = NULL) const;

status_t				SendMessage(BMessage* message,
										BHandler* replyTo = NULL,
										bigtime_t timeout
											= B_INFINITE_TIMEOUT) const;

status_t				SendMessage(BMessage* message,
										BMessenger replyTo,
										bigtime_t timeout
											= B_INFINITE_TIMEOUT) const;

status_t				SendMessage(uint32 command,
										BMessage* reply) const;

status_t				SendMessage(BMessage* message,
										BMessage* reply,
										bigtime_t deliveryTimeout
											= B_INFINITE_TIMEOUT,
										bigtime_t replyTimeout
											= B_INFINITE_TIMEOUT) const;
  • Sends a message to the target.
    • message: Pointer to the message to send.
    • reply: Pointer to a message to receive a reply.
    • timeout: Time to wait for a reply before timing out.

1 Send Message with Command:

status_t SendMessage(uint32 command, BHandler* replyTo = NULL) const;
  • Sends a message with a specified command to the target.
    • command: The command to send.
    • replyTo: Pointer to a BHandler to receive a reply.

2 Send Message with Message and Reply Handler:

status_t SendMessage(BMessage* message, BHandler* replyTo = NULL, bigtime_t timeout = B_INFINITE_TIMEOUT) const;
  • Sends a message to the target.
    • message: Pointer to the message to send.
    • replyTo: Pointer to a BHandler to receive a reply.
    • timeout: Time to wait for a reply before timing out.

3 Send Message with Message and Reply Messenger:

status_t SendMessage(BMessage* message, BMessenger replyTo, bigtime_t timeout = B_INFINITE_TIMEOUT) const;
  • Sends a message to the target.
    • message: Pointer to the message to send.
    • replyTo: BMessenger object to receive a reply.
    • timeout: Time to wait for a reply before timing out.

4 Send Message with Command and Reply Message:

status_t SendMessage(uint32 command, BMessage* reply) const;
  • Sends a message with a specified command to the target.
    • command: The command to send.
    • reply: Pointer to a message to receive a reply.

5 Send Message with Message and Reply Message:

status_t SendMessage(BMessage* message, BMessage* reply, bigtime_t deliveryTimeout = B_INFINITE_TIMEOUT, bigtime_t replyTimeout = B_INFINITE_TIMEOUT) const;
  • Sends a message to the target.
    • message: Pointer to the message to send.
    • reply: Pointer to a message to receive a reply.
    • deliveryTimeout: Time to wait for the message to be delivered before timing out.
    • replyTimeout: Time to wait for a reply before timing out.

Complete Example:

#include <Messenger.h>
#include <Handler.h>
#include <Looper.h>
#include <Message.h>
#include <stdio.h>
#include <unistd.h>

class MyHandler : public BHandler {
public:
    MyHandler() : BHandler() {}

    virtual void MessageReceived(BMessage* message) {
        printf("Message received\n");
        
        int32 value;
        if (message->FindInt32("value", &value) == B_OK) {
        		printf("Received value: %ld\n", value);
        } else {
        	printf("Failed to receive value\n");
        }
    }
};

int main() {

    MyHandler* handler = new  MyHandler();
    BLooper* looper = new BLooper();
    looper->AddHandler(handler);
    
    // Start the event loop
    looper->Run();

    BMessenger messenger(handler, looper);

    // 1. Send message with command
    messenger.SendMessage('CMD1');

    // 2. Send message with message
    BMessage message('MAIL');
    message.AddInt32("value", 7);
    messenger.SendMessage(&message);

    // Wait for a while to allow messages to be processed
    usleep(10000000);

    return 0;
}

Output:

image-114.png