Initialization of the MasterController (Android/Kotlin and Flutter)
We communicate with the Master Controller by sending and receiving events. To simplify this process in our demo app, we use a wrapper class called MasterControllerAdapter for Swift, MCHandler for Kotlin, and McWrapperHandler for Flutter in our example app. These classes provide methods for interacting with the Master Controller, so we do not need to understand all the intricacies of its implementation.
Here are some important points about the MasterControllerAdapter/MCHandler/McWrapperHandler class:
- It serves as a single point of connection with the Master Controller, preventing the need to scatter connection code throughout the app.
- It offers dedicated methods for sending events.
- It provides a single method for receiving events from the Master Controller.
- Observers can be added or removed to handle received events in different classes as needed.
Android/Kotlin Implementation
We mainly use three methods to communicate with the Master Controller:
-
postEvent(event: EventFrameworkEvent): This method sends an event to the Master Controller. We use it when we expect a result event after triggering an event on the Master Controller side. By implementing the then block on postEvent, we can retrieve and handle the result event. The postEvent() method returns a Future object, and its get() method returns an EventFrameworkEvent, which is the superclass of all events.
-
forwardEvent(event: EventFrameworkEvent): Similar to postEvent(), this method sends events to the Master Controller. However, it operates as a "Fire and Forget" mechanism, meaning it does not expect a result event for the triggered event. We use this method when no result event is expected. Here you can find the events table.
-
executeEvent(eventFrameworkEvent: EventFrameworkEvent?): This method receives events from the Master Controller.
MCHandler extends from SynchronousEventHandler (wrapper class available in MasterController library) which makes the methods for communicating with MasterController available to MCHandler.
class MCHandler private constructor(
private val context: Context
) : SynchronousEventHandler(context) {
private var mcEventHandler: SynchronousEventHandler? = null
/**
* Holds MCEventObserver to notify about Master Controller events
*/
private var observers: MutableList<MCEventObserver> = mutableListOf()
}
MCHandler is a singleton class with a getInstance() method that allows us to obtain its instance. When we create an instance of MCHandler, the constructor of SynchronousEventHandler is called, executing the necessary steps to initialize the Master Controller.
companion object {
private var instance: MCHandler? = null
fun getInstance(context: Context): MCHandler {
synchronized(this) {
if (instance == null) {
instance = MCHandler(context)
instance?.setEventHandlerInstance(instance!!)
}
return instance!!
}
}
}
fun setEventHandlerInstance(eventHandler: SynchronousEventHandler) {
this.mcEventHandler = eventHandler
}
MCHandler class extends the SynchronousEventHandler class, which is an abstract class. Therefore, we need to override a method called executeEvent. This method is invoked whenever the Master Controller sends any event, meaning it is responsible for receiving events from the Master Controller.
@Synchronized
override fun executeEvent(event: EventFrameworkEvent?) {
if (observers.isNotEmpty()) {
observers.forEach { observer ->
event?.let {
observer.onEventReceived(it)
logDebug(
observer.javaClass.name + " is notified by " + observer.javaClass.name,
"processEvent",
TAG
)
}
}
} else {
logDebug(
"No observer attached to received event ==> $event",
"processEvent",
TAG
)
}
}
We need to add an event observer to get the callback in your current class whenever Master Controller sends any event. There is a method named addMCEventObserver which is responsible for adding an event observer.
/**
* Adds MCEventObserver instance to observers List.
*
* @param o - MCEventObserver instance
*/
@Synchronized
fun addMCEventObserver(o: MCEventObserver) {
observers.add(o)
logDebug(
o.javaClass.name + " has been added as MC Event observer",
"addMCEventObserver",
TAG
)
}
NOTE: It is important to remove the observer whenever we no longer need to listen for MasterController events.
There is a method called removeMCEventObserver that handles the removal of the observer.
/**
* Removes MCEventObserver instance from observers List.
*/
@Synchronized
fun removeMCEventObserver(observer: MCEventObserver) {
observers.remove(observer)
logDebug(
observer.javaClass.name + " has been removed as MC Event observer",
"removeMCEventObserver",
TAG
)
}
Android/Kotlin Usage Examples
Below is a code snippet demonstrating how to use the provided methods in their respective implementation classes: Here is a code snippet for obtaining the instance of MCHandler:
mcHandler = MCHandler.getInstance(context of current Activity);
By adding an event observer to MCHandler, we can start listening to MC events. Below is a code snippet for adding an event observer:
mcHandler?.addMCEventObserver(reference of MCEventObserver)
Following is the callback method where you receive a call when the MasterController sends any event (if an observer is added).
override fun onEventReceived(eventFrameworkEvent: EventFrameworkEvent) {
// Write code to handle the event
}
By removing the observer we can stop listening to MC events. Here is the code snippet:
mcHandler?.removeMCEventObserver(context of Activity)
Below is the diagram to understand the flow mentioned above in the form of code:
Flutter/Dart Implementation
For Flutter, we use McWrapperHandler in our demo app, which provides similar functionality to the Android MCHandler. The McWrapperHandler class serves as a wrapper around the McWrapperApi and provides methods for communicating with the Master Controller.
Key Features of McWrapperHandler:
- Singleton Pattern: Ensures a single instance throughout the app lifecycle
- Event Sending: Uses the
send()
method to communicate with Master Controller - Event Receiving: Implements
McWrapperApiEventReceiver
to handle incoming events - Observer Pattern: Allows multiple observers to listen for Master Controller events
- Lifecycle Management: Provides start, stop, suspend, and resume functionality
class McWrapperHandler extends McWrapperApiEventReceiver {
late McWrapperApi _mcWrapperApi;
static final McWrapperHandler _instance = McWrapperHandler._internal();
List<McWrapperApiEventReceiver> receivers = [];
McWrapperApi get api => _mcWrapperApi;
factory McWrapperHandler() {
return _instance;
}
}
Flutter/Dart Usage Examples
Getting Instance and Initialization
// Get the singleton instance
final mcHandler = McWrapperHandler();
// Or get from service locator (see below for further details)
// final mcHandler = locator<McWrapperHandler>();
// Initialize the wrapper
await mcHandler.initializerMethod();
Sending Events to Master Controller
Future<void> sendEventToMC() async {
try {
// Create your event
final startEvent = StartEventT();
// Send event and handle response
final response = await mcHandler.send(startEvent);
response.fold(
(error) {
print("❌ Error sending event: $error");
},
(result) {
print("✅ Event sent successfully: $result");
// Handle the result event
},
);
} catch (e) {
print("❌ Exception: $e");
}
}
Adding Event Observers
class MyEventReceiver extends McWrapperApiEventReceiver {
@override
void onReceive(Object event) {
print("📥 Received event: ${event.runtimeType}");
// Handle specific event types
if (event is TriggerBannerEventT) {
handleTransactionEvent(event);
} else if (event is StartResultEventT) {
handleStartResult(event);
}
}
void handleTransactionEvent(TriggerBannerEventT event) {
// Handle transaction events
}
void handleStartResult(StartResultEventT event) {
// Handle start result events
}
}
// Add the observer
void setupEventHandling() {
final mcHandler = locator<McWrapperHandler>();
final eventReceiver = MyEventReceiver();
mcHandler.addObserver(eventReceiver);
}
Removing Event Observers
void cleanupEventHandling() {
final mcHandler = locator<McWrapperHandler>();
final eventReceiver = myEventReceiverInstance;
mcHandler.removeObserver(eventReceiver);
}
Lifecycle Management
class MyApp extends StatefulWidget with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final mcHandler = locator<McWrapperHandler>();
switch (state) {
case AppLifecycleState.resumed:
mcHandler.resumeWrapper();
break;
case AppLifecycleState.paused:
mcHandler.suspendWrapper();
break;
case AppLifecycleState.detached:
mcHandler.stopWrapper();
break;
default:
break;
}
}
}
Flutter Service Locator Integration
In Flutter, we use a service locator (GetIt) to manage dependencies and provide global access to the McWrapperHandler instance. This approach offers several benefits:
- Singleton Management: Ensures only one instance of McWrapperHandler exists throughout the app
- Global Access: Any part of the app can access the handler without passing it through constructors
- Dependency Injection: Simplifies testing by allowing easy substitution of mock implementations
- Lifecycle Management: Centralized control over when services are created and destroyed
- Decoupling: Reduces dependencies between classes by providing a central registry
GetIt locator = GetIt.instance;
Future<void> setupDependencies() async {
// Initialize McWrapperHandler
final mcHandler = McWrapperHandler();
await mcHandler.initializerMethod();
// Register in service locator as singleton
locator.registerSingleton<McWrapperHandler>(mcHandler);
// Add observers for transaction handling
final transactionReceiver = TransactionEventReceiver();
mcHandler.addObserver(transactionReceiver);
locator.registerLazySingleton(() => transactionReceiver);
}
// Access from anywhere in the app
void useFromAnywhereInApp() {
final mcHandler = locator<McWrapperHandler>();
// Use the handler...
}
Important Notes
NOTE:
- Always remove observers when they are no longer needed to prevent memory leaks
- Handle app lifecycle events to properly suspend/resume the wrapper