HelloWorld, Deconstructed
In our beginners tutorial called Creating Your First Application we skipped over many features of the "Hello World" application that we asked you to paste into the IDE. In this tutorial we are going to take a much deeper look at that code, examine the basic structure of a C++ application, and discuss some basic design decisions we have made in the MoSync approach to mobile application development.
- Hello World (Naked)
- Including Library Classes
- Using Namespaces
- Inheriting From a Base Class
- Declaring Public Members
- Defining a Constructor
- Using Syscalls
- Grouping Statements With Brackets
- Detecting Key Presses
- Closing Applications
- Detecting Other Events
- Commenting Code
- Defining the Application Start Point
- Running our Moblet Application
- Returning Results
- Hello World (Fully Clothed)
- What Next?
A note to absolute beginners: C/C++ applications can be a little overwhelming at first, but with a little persistence you will soon get the hang of it. As you go through this tutorial, try typing each line into the MoSync IDE, rather than just cutting and pasting. That way you''ll get a better feel for laying out the code and getting the syntax right. As you type, you will be given interactive help by the IDE, which can be very useful. Remember to use the Moblet Project template when you create you project.
A note to experienced C++ developers: You probably know a lot of this stuff already, so feel free to skip ahead to our extensive collection of tutorials and example applications. On the other hand, if you do read on, maybe you will learn something important you didn't know about C/C++ or MoSync, like that tricky MAMain entry point....
Hello World (Naked)
Here is the code we asked you paste into the MoSync IDE in our Creating Your First Application tutorial:
#include <MAUtil/Moblet.h>
using namespace MAUtil;
class MyMoblet : public Moblet
{
public:
MyMoblet()
{
maSetColor(0xFFFFFF),
maDrawText(0, 32, "Hello World!");
maUpdateScreen();
}
void keyPressEvent(int keyCode, int nativeCode)
{
if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
{
close();
}
}
void keyReleaseEvent(int keyCode, int nativeCode)
{
}
};
extern "C" int MAMain()
{
MyMoblet myMoblet;
Moblet::run( &myMoblet );
return 0;
}Now let's take a close look at what is going on in each of these lines of code.
Including Library Classes
The program starts with a directive to include the contents of the Moblet.h header file from MoSync's MAUtil directory:
#include <MAUtil/Moblet.h>
In C/C++, header files are an important way to connect together the different files of a program. It is common practice to put the generic code that you might want to use in many places in a separate file and two create a header file that contains forward definitions (declarations of classes, functions, and data types) for that generic code. Then you use an #include statement to include the header file in your other code modules. The header file acts like an interface to your generic code. That way you can reuse your generic code in many places, but only have to make updates to it in one place. For information, see Working with MoSync Libraries.
Here we are using a header file to connect our main application file with the pre-compiled library file (mautil.lib) that contains the generic code for Moblets.
The "#" sign indicates that this directive is to be handled the preprocessor — a program that will be called by the compiler as the first step of translating our source code into an executable binary file. In this case the directive instructs the preprocessor to read in the declarations in Moblet.h. We will explain what a Moblet is, and how you use it, a little later on.
Using Namespaces
Our next statement specifies that we will be using the MAUtil namespace as the base reference for our objects:
using namespace MAUtil;
The effect of using this namespace is to say "look in the set of things called "MAUtil" to find the definitions of the identifiers I use in this program". By specifying the namespace here our code becomes more readable. If we didn't have this line, we would have to explicitly reference the scope of MAUtil objects, we would have to write MAUtil::Moblet every time we wanted to use the Moblet class.
Inheriting From a Base Class
The next thing we need to do in our application is define its classes. Here we define a new class, based on the Moblet class, called MyMoblet:
class MyMoblet : public Moblet {Moblet is a MoSync base class - a class which is meant to be built upon to do something useful. In the C++ world, a class is essentially a set of data and functions which manipulate that data (often referred to as instance variables and methods in other object-oriented languages). A class is a blueprint: in a running application the class is used to create actual objects (instances) as they are needed.
Usually a class represents something concrete — although it's sometimes a little hard to understand what that thing is. In the case of Moblet it represents a simplified event handler for a mobile device.
As we will see later, Moblet takes care of the application main loop for you. All you need to do is subclass it and implement functions for the events that you want to your program to respond to (such as key presses, screen touches, and new connections). A Moblet application responds to events in a consistent way regardless of the device it runs on. That means we do not need different code for an Android device, Symbian device, a Windows Mobile device, or a phone running Java ME.
To learn more about the events that Moblet can detect, and the various types of listeners you can set up, look up Moblet in the MoSync API Reference Guide in the IDE under Help > API Reference > Classes > MAUtil::Moblet.
Our new class, MyMoblet inherits all the chracteristics of the Moblet base class, and adds to it. What we will be adding are functions that will perform actions when events occur.
The keyword "public" before the Moblet identifier means that other classes and functions in our application can access all the public members of the Moblet base class when using MyMoblet. That is to say, what is defined as public in Moblet becomes public in MyMoblet, even if we don't get around to listing them in MyMoblet.
Declaring Public Members
The data members (instance variables) and member functions (methods) within a class can be either public or private. If they are in the public section of the class, they can be accessed from other classes. Here we start the list of public members of our MyMoblet class:
public:
Defining a Constructor
The first public member of our MyMoblet class is a method, the constructor. The constructor gets called when an instance of the class (an object) is created. In this case it is a parameterless constructor as we don't need to pass in any data:
MyMoblet() {(In the case of MyMoblet there is no destructor to clean up after the object once it has fulfilled its usefulness, simply because it would be empty. Constructors and destructors can be implicit: if you don't specify a constructor or destructor, the compiler will put it in.)
Using Syscalls
The MyMoblet constructor contains three function calls which will be executed in sequential order. (You can distinguish function calls from data members in a class because they are immediately followed by 0, 1, or more parameters in parentheses.)
These three function calls - we call them syscalls - are to low-level, device-independent functions that are defined in the main MoSync syscall header file, maapi.h. You'll be using the functions defined in maapi.h a lot in your applications, so it's worth getting to know it intimately. Read about maaip.h syscalls in the MoSync API Reference Guide in the IDE under Help > API Reference > Files > maapi.h.
The first syscall sets the colour of the output to white:
maSetColor(0xFFFFFF);
From now on, until it is explicitly changed, all output from text and drawing commands will be displayed in this colour. Note that each statement within the method is terminated by a semicolon in C/C++.
The second syscall in the MyMoblet constructor renders the text "Hello World!" to the backbuffer at the coordinates x=0 and y=32:
maDrawText(0, 32, "Hello World!");
The coordinates 0,0 are the top left corner of the screen. The y-axis increases as you head downwards, the x-axis increases as you head rightwards.
To make screen update flicker free, drawing is done to a so called backbuffer, which later, when everything is drawn, will be copied in to the display. Thus the text is not yet visible at this point in the program.
The third syscall is what updates the physical screen of the device with the contents of the backbuffer:
maUpdateScreen();
If you forget this last statement, and that's easy to do, you would just see a blank screen when you ran your application!
Grouping Statements With Brackets
Curly brackets are used to group statements in C/C++. Now that we are at the end of the constructor, we close the opening curly bracket at the beginning:
}
(The bracket that marks the beginning of the class will be closed it later on, when our class is complete.)
Detecting Key Presses
The second public method of our MyMoblet class is keyPressEvent:
void keyPressEvent(int keyCode, int nativeCode) {Before the name of the method, we need to state the type of the data that the method returns. In this case, the method does not return anything ("void" = nothing).
The keyPressEvent method is called whenever a key is pressed on the device's keypad. It is passed two parameters by the MoSync runtime — the runtime is that part of MoSync which interfaces with the device's operating system and which handles the syscalls from your application.
The first parameter is a device-independent code representing a key, a MAK code. Regardless of the device, if the 0 key on the device's keypad is pressed, the keycode will be MAK_0, and if the device has a back key it will be MAK_BACK.
You can find all the MAK codes in the MoSync API Reference Guide in the IDE under Help > API Reference > File Members > Defines > M.
The second parameter is device-specific (native) key code. That's needed because the MoSync MAK codes are not an exhaustive list of all the buttons ever made available on a mobile device, instead they are a common subset. Your phone may have a button which is specific to that model, for instance the volume keys or the back button on an Android phone. If you know what the value of those keys, you can act on them here.
The keyPressEvent method is one of several methods we have inherited from the Moblet base class. Here we replace (override) the method with our own version. When the program runs and an instance of MyMoblet is created, the C++ runtime system will detect that the instance has a method that replaces the method in the base class. Methods that can be overridden like this are called "virtual methods" in C++.
Closing Applications
Now we define what should happen if the zero key on the keypad has been pressed (or the back button, or the soft-right key), and in this case we are just going to exit the program using Moblet's close function:
if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
{
close();
}
}The close method releases any resources used by the object and ends the execution of the program.
The Moblet base class also includes a closeEvent method. That method can be used to detect a forced application close detected by the MoSync runtime on the device. By overriding it in your Moblet-based class, you can perform essential data-saving tasks - you only have a second or so! - before your application is forced to terminate by the devices OS.
Detecting Other Events
There are many other event detection methods in the Moblet base class that you can override, including pointer movements, focus changes, Bluetooth connection, timers, and so on. The keyReleaseEvent which comes next in our example is another method that you will commonly want to implement in a Moblet-based application. It isn't actually needed for this simple application, but it works in a similar way to the keyPressEvent we used above.
void keyReleaseEvent(int keyCode, int nativeCode) {For a complete list of the events See the MoSync API Reference Guide in the SDK under Help > API Reference > Classes > MAUtil::Moblet.)
Commenting Code
We've left a comment which you can replace with your own code for key release events:
// todo: handle key releases - put your code here.
}Single line C/C++ comments are preceded by two forward slash characters // and terminate at the line end. Anything on the right side of both forward slashes would not be read by the compiler (actually the parser).
You can also use a single forward slash followed by an asterisk to indicate the start of a comment. To end the comment, type an asterisk followed by a forward slash, for example:
/* This is a comment */
This type of comment can be spread over several lines.
There are no private data members or functions needed for the MyMoblet class, so we can just close its definition now. Note that we need to put a semicolon after bracket that closes a class.
};
Now we have completed our definition of the MyMoblet class, which is the only class we need in this simple application. If we needed more classes, we would put them here.
Defining the Application Start Point
We move on to defining the entry point of our program. This is the place where the execution of the application starts when it is run:
extern "C" int MAMain() {In C you always have a function called main() that is called when the program execution starts. In a MoSync application, the main() function is actually implemented in the runtime. (Remember, that's the part of the application that interfaces with the device's operating system, and hides the complexity of the OS from your application.) In your application you need to use MAMain() to mark its start point. Just think of MAMain() as a synonym for main().
Because C++ is an object-oriented programming language which includes (and is built upon) the procedurally-oriented C language, C++ programs can consist of both C++ classes (like MyMoblet) and C statements like this one. Unlike our earlier methods, this one is a C function (hence the extern "C" bit). A function is like a method, but is not associated with an object, and is defined outside of the classes in the program. In C++, you can freely mix between C and C++ but you always have to start your applications in C.
Running our Moblet Application
There is a static method in the Moblet class called run (a static method is associated with the class, not with any instance). This is what starts your application. To call the run method you need to pass it a pointer to a new instance of our MyMoblet class, so we create one with the "new" keyword:
Moblet::run(new MyMoblet());
This constructs the MyMoblet instance and runs the code in the MyMoblet constructor.
Returning Results
C main methods should always return an integer (int), so we return 0, by which we indicate that the program has successfully ended:
return 0; }
And that's it. Our application is now ready to build and run.
Hello World (Fully Clothed)
/**
* This application provides a very basic example of how to use MoSync
* to print the text "Hello World" to a device's screen. The code is very
* well commented so that you can see exactly what's happening at each step.
* The application makes use of MoSync's Moblet framework to handle events.
*
* @file helloworld.cpp
*
* @author Chris Hughes
*/
/*Include the header files for MoSync Moblets so that we can
*access the Moblet library code from our application.
*/
#include <MAUtil/Moblet.h>
/*Declare the MAUtil namespace so that we can use the short forms of
*identifiers in our code. This allows us to write, for example, "Moblet"
*instead of "MAUtil::Moblet".
*/
using namespace MAUtil;
/*Create the wrapper for the entire application. It is here that we will manage
*the application and handle events. To create the wrapper we make our own
*implementation of the MoSync Moblet base class. There can be only one Moblet
*in an application.
*/
class MyMoblet : public Moblet
{
// Define our new class's public methods.
public:
/*First, the constructor, which we will call whenever we need an
*instance of MyMoblet. In this case, the constructor consists
*of three syscalls.
*/
MyMoblet()
{
// The first syscall sets the background colour.
maSetColor(0xFFFFFF);
// The second syscall writes the text "Hello World" to the backbuffer.
maDrawText(0, 32, "Hello World!");
// The third syscall copies the contents of the backbuffer to the
// physical screen.
maUpdateScreen();
}
/*Next, a method for detecting key presses. This is a method we have
*inherited from the Moblet base class and here we will override that
*method with some processing of our own.
*/
void keyPressEvent(int keyCode, int nativeCode)
{
//Close the application if the zero, back, or soft-right key is pressed.
if(keyCode == MAK_0 || keyCode == MAK_BACK || keyCode == MAK_SOFTRIGHT)
{
close();
}
}
// Finally, a code stub we might need to use later for another event type.
void keyReleaseEvent(int keyCode, int nativeCode)
{
// to do: handle key releases - put your code here.
}
};
/*Now we get to the entry point for the application - the place where
*processing starts.
*/
extern "C" int MAMain()
{
// Create the instance of MyMoblet
MyMoblet myMoblet;
// Run the Moblet to start the application.
Moblet::run( &myMoblet );
/*MyMoblet will run until it is closed by the user pressing key 0. When
*it's closed we end our program in a well-behaved way by returning zero.
*/
return 0;
}What Next?
If you are new to C++, there are many useful references and tutorial sites on the Internet that can help you learn more. We particularly recommend the C++ language tutorials at http://www.cplusplus.com/doc/tutorial.
We have several other beginner's application examples which you can find in the MoSync IDE, for example: HelloWorld, HelloMoblet, HelloNativeUI, HelloOpenGLES.
We have a more advanced example of a Moblet application in our programmer's guide Event-Driven OO Applications, and in our tutorial Starting a New Moblet Project.
- Printer-friendly version
- Login or register to post comments
Share on Facebook
Very nice tutorial great
Very nice tutorial great explanation keep going keep soaring thumbs up MoSync Team :D