Message handling under HTML5 with Smart Mobile Studio
In my previous post I informed you that my latest addition to the QTX library is WinAPI like messages. It’s important to underline that this has nothing to do with SMS text-messages, push messages or anything like that; rather it deals with sending messages between forms, controls and routines on an application-wide scale.
Thankfully HTML5 comes with a message stack built in, which we have made full use of in our API.
The Windows programming API is based on messages. In fact message-handling is the building block of any Microsoft Windows application. If you come from a .net or Visual Basic background you have been shielded from this – but C++ and Delphi programmers will know what I’m talking about. Native Windows programming is all about message dispatching and consumption.
Messages represents a very powerful technology. Actually there are some chores that can be said to be impossible to achieve without them. Being able to broadcast messages which every single class or component in an application, regardless of their relationship or knowledge of each other, are capable of receiving and processing.. well, it goes without saying that messages solves a lot of tough problems.
Note: Messages are great but they are also very easy to abuse! Always make sure you use messages with care, or you may risk turning your mobile app into a proverbial debugging nightmare.
Before we look at how to use messages, let’s take a look at what exactly a message is. The message unit is names qtx.msgport.pas and can be found in the quartex library folder. Simply check this out from SVN and copy it to your libraries folder. There is a shortcut to this folder under the start-menu item for Smart Mobile Studio.
If you open up this file you will find the message defined as such:
Right, let’s step through the class field by field..
The ID field
The ID field of the message is just that, the message-id. This can be anything you want. I suggest you define your message ID’s using constants – and also that you start at some +1000 number. Please note that ID 0 through 999 is reserved for Smart Mobile Studio (!).
In the unit there is a constant called CNT_QTX_MESSAGES_BASEID which you can use to define the lower limit of your application specific messages. Below is how I typically define my own messages. Starting CNT (constant), then the name of my application – followed by the name of the functionality. And I always start from CNT_QTX_MESSAGES_BASEID;
const CNT_MYAPP_DATALOADING = CNT_QTX_MESSAGES_BASEID + 1; CNT_MYAPP_DATALOADED = CNT_QTX_MESSAGES_BASEID + 2; CNT_MYAPP_CALCSTARTED = CNT_QTX_MESSAGES_BASEID + 3;
The source field
The source field is special. HTML5 messages require that you include the source-url from where the message came. This may seem odd, but it makes perfect sense when you realize that messages can be sent between open browser windows. The application running in one window may not come from the same server as the second window. Hence you have to populate this with the current URI.
If you dont care about checking the URI you can simply use “*”, this allows your message to pass though anyway. The security of URI checking is for your benefit, it’s not included against you. Keep that in mind.
The data field
But this also means that our generated code functions more or less like Delphi assembly. Every method in a TObject derived class expects to have a hidden parameter, namely the “self” parameter. Which is a reference to a structure in memory which represents the current object instance. This is not problematic for JSON, but the RTTI info which accompany each method is (!) because RTTI identifiers, which are unique each time you execute an application, will also be duplicated.
So the rule is, that whatever you want to serialize should inherit from JObject, which is a parent-less object without the OOP mechanisms we know and love from Delphi and C++ builder (read: without all that extra data).
But you dont need to know about all that technical mumbo-jumbo. Let’s define a hypotethical message you want to use in your application. It goes like this:
TMyComplexMessage = class(JObject) public Property FirstName:String; Property LastName:String; Property Age:Float; Property Birth:TDateTime; function Deserialize:String; procedure Serialize(value:String); procedure FromMessage(const msg:TQTXMessageData); function ToMessage:TQTXMessageData; end;
The art of turning an object into a JSON structure (or XML for that matter) is called “deserialization”, and not unexpectedly the reverse, turning a JSON string into an object is cleverly called “serialization”. So for simplicity I decided to call our methods just that. What these methods do is take the current object and either turn that into JSON text or, take a JSON text and populate our object with it’s values.
You may think this is overkill, but I tell you, writing good software is all about structure, order and maintainability. The more time you spend on the infrastructure the better your software will be. That is a fact of life.
Next we have the FromMessage and ToMessage, these are just helper methods that allows us to skip the nitty-gritty in our program code, and enables us to send and handle messages with a single line.
The code for these methods looks like this:
procedure TMyComplexMessage.FromMessage(const msg:TQTXMessageData); begin serialize(msg.Data) end; function TMyComplexMessage.ToMessage:TQTXMessageData; Begin result:=new TQTXMessageData(); result.ID:=CNT_MYAPP_MyComplexMessage; result.source:="*"; result.data:=Deserialize; end; function TMyComplexMessage.Deserialize:String; begin result:=JSON.stringify(self); end; procedure TMyComplexMessage.Serialize(value:String); var mData: variant; begin mData:=JSON.Parse(Value); self.FirstName:=TMyComplexMessage(mData).FirstName; self.LastName:=TMyComplexMessage(mData).LastName; self.Age:=TMyComplexMessage(mData).Age; self.Birth:=TMyComplexMessage(mData).Birth; end;
As you can see, it’s all fairly straight forward code. You can even isolate these methods in a base-class and just override Serialize() in each derived type, since that’s where the differences will be.
Sending our message
We are now ready to send our message, but let’s start in reverse – by implementing the receiver first!
FEventHandler:=TQTXMessageSubscription.Create; FEventHandler.SubScribe(CNT_MYAPP_MyComplexMessage,procedure (Message:TQTXMessageData) var mObj: TMyComplexMessage; begin mObj:=new TMyComplexMessage(); mObj.serialize(message.data); // "mObj" is now ready to be used! end);
Sending a message is simplicity itself, especially with our serialization methods firmly implemented:
var mMessage:TMyComplexMessage = new TMyComplexMessage(); mMessage.firstname:="jon"; mMessage.lastname:="aasenden"; mMessage.age:="40.9"; QTX_PostMessage(mMessage.toMessage);
That’s basically how easy it is to send complex messages. You can ofcourse send smaller stuff, if all you want to do is transport a text-string between two components, then creating a complex datatype (message class) is overkill. But if you are writing a serious application you really want to isolate everything in proper classes. It’s so much easier to maintain and extend later.
Messages to solve problems
If you have spent a couple of years in Delphi you know perfectly well that the VCL (Visual Component Library) which has been Delphi’s bread and butter for nearly two decades, is littered with message-use.
Everything from database states to refreshing a GUI elements is handled by messages. In fact, if you take a closer look at TCustomControl – which is the primary control that most visual controls inherit from, you will see that it functions, in company with it’s ancestor TWinControl, as the proverbial switch-box for messages. It catches all the relevant messages from the OS, like key-presses, redraw requests, movement – you name it, and isolates these in event procedures.
I have no desire for Smart Mobile studio to go the same way. It could, ofcourse, but there is little benefit in doing so since the difference between the browser environment and the WinAPI environment is like night and day.
For example: You have 3 TW3Edit boxes connected to a database-field. Whenever the data in the table changes, all the visual controls should reflect that by changing as well.
Without messages all controls would have to register with the data-source and all types of updates would be represented by a call mechanism. This is perfectly valid, but it represents a lot more code (and potential errors) than using messages to solve the problem.
All connected controls could be notified with something as simple as:
mMsg.db := FDB.name; mMsg.Table := FDB.Table.Name; mMsg.FieldName := FDB.Table.Field[mChanged].Name; mMsg.Value := FDB.Table.Field[mChanged].asVariant; QTX_BroadcastMessage(mMsg.serialize);
The messages are automatically filtered out by it’s ID, and later by the content of the message. Each message handler only consume one particular type of message – hence the cost of broadcasting is minimal. The “sender” field in this case (as explained at the beginning of this article) would serve as the namespace. Hence different forms with the same name on their controls and listening to the same field would be clearly separated.
So if Form1 is set to receive updates on 3 controls, and Form2 is inactive but set to receive the same updates — then only Form1 will get the updates. This is controlled by the application dispatcher.
In short: You can expect some pretty cool controls in the future 🙂
Well, hope you found this little introduction helpful — now go download QTX 🙂
The vatican vault
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012