×
Namespaces

Variants
Actions
Revision as of 01:21, 20 July 2012 by lorion84 (Talk | contribs)

Optimising Nokia Asha Web Apps for speed by eliminating server round trips

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to write a highly responsive Series40 WebApp. It uses a Minesweeper clone as example.

Note.pngNote: This is an entry in the Asha Touch Competition 2012Q3

Article Metadata
Code Example
Source file: Nokia Project
Installation file: http://eve-c.org/minesfinder
Tested with
Devices(s): Nokia C3-01, Nokia-C2-01
Compatibility
Platform(s): Series 40
Series 40
Device(s): All
Article
Keywords: HTML, JavaScript, CSS, MWL, UX
Created: lorion84 (24 Jul 2012)
Last edited: lorion84 (20 Jul 2012)

Contents

Introduction

If you are writing a Series 40 Web App, you are prepared to cater for the low end of mobile phones. Knowing that the devices which will run your app are very basic should not stop you from trying to deliver a high-end user experience. It is more the other way around, knowing that those phones have limited capabilities should encourage you to use every trick to provide your user with a premium feeling.

In my eyes user experience (UX) has three big topics:

  1. Function - what does the app do, how bug-free is it, etc...
  2. Design - how does the app, and the UI, look
  3. Responsiveness - how does it feel using the app, how fast is it


Of course, there fields have no 100% hard borders. They affect each other and you have to think about all aspects, but I will concentrate on number 3, the responsiveness. And within this part, I concentrate on the speed of the application. To be even more concrete, I'll talk about reducing these so called "browser round-trips".
Reducing those round-trips has the highest priority if you try to speed up your app. A round-trip takes almost as much time as opening the app. If the user has to wait 2-3 seconds after every single click the does, he won't be very satisfied with the experience. In addition, every round-trip is a possible point of error. If the user has a bad internet connection, a round-trip can break the app.
To make sure that this is not only gray theory, I created a Minesweeper clone, MinesFinder. You can find the source in the Nokia Project . I try to get it into the Nokia Store. Until then you can visit [[1]] with your Nokia Browser to play it.
You can play the game without a single round-trip. You can flag fields, dig for mines, get a "You Loose" message if you hit a mine and a "You Win" message if you have flagged all mines correctly. In addition there is a counter, showing how many flags you have already planted.

1. Use MWL where ever you can.

When ever you try to execute any piece of JavaScript, the app will have to make round-trip. Unless you use Nokia's Mobile Web Library. The commands from this library can be executed locally. For more details, check the Library entry for MWL. I recommend to have this documentation always close to you. Using mwl.hide("#object"); instead of $("#object").hide(); makes already a huge difference.
This is a pretty basic tip, but mastering the MWL is important for the following tricks as well. Since only MWL gives you the chance to do anything without a round-trip.

Tip.pngTip: You can extract the Mobile Web Library from the installation of the Web Tools. Just search for mwl.js. You can then use the library in other, non Nokia Browser based, projects and increase your familiarity with it.


2. Use JavaScript like a server-side scripting language.

Coming from "traditional" web apps, JavaScript is often used to pull in new information when needed (e.g. via an AJAX request). With a Series40 WebApp, every AJAX call and every newly created DOM element will need a server round-trip. So it is often better to load everything that could be needed upfront while the app is starting. Hide it and show it only if needed. For this, you need the command document.write("Some string...");, which is pretty uncommon nowadays. The following code is used to create 100 DIV-tags which act as counter for the flags.

<script type="text/javascript">
document.write('<div id="counter" class="inline">');
document.write('<div class="inline">10</div>');
for (cc=9; cc>-91; cc--)
{
document.write('<div class="ui-hide">' + cc + '</div>')
}
document.write('</div>');
</script>

Tip.pngTip: Use loops and document.write(); to increase the readability of your source code and to reduce the risk of copy/paste/oversight errors.

Tip.pngTip: MWL can only select IDs. Every item which should be accessible needs an ID tag. Add the context to the ID to improve readability: mainpageTab2Button1

Warning.pngWarning: While pre-caching small text elements is no problem, you should be careful with loading images in bigger numbers.


Using document.write(); is not restricted for the body, you can also use it to create CSS blocks in the header or load script files on start,

3. Use CSS instead of program logic.

This point combines the 1. and 2. and adds the idea that you move as much logic as possible into CSS which can be checked and manipulated by MWL. Let me explain this further by three examples:

1. Quantity, not Logic

Since the Nokia Browser cannot even execute if () then {} else {} statements without a round-trip, you have to move the logic into the initial loading. In the MinesFinder example, we have two possible actions: digging and flagging. My first idea was that the user has a button to choose whether he wants to dig or to flag. He would then click one game field and the action takes place. This is not possible without a round trip since you would need to check the dig/flag mode. I moved the logic into the initial loading by not having one dig/flag button and one table with the game fields. Instead the dig/flag button became a toggle for two tabs. Every tab contains a table with the game fields. In addition, every diggable/flaggable field is represented by three different DIVs which are shown according to the game fields state (empty, digged, flagged). For 100 clickable fields I had to create 600 divs. The following code sniped shows the creation of the table which lets the user dig for mines:

<script type="text/javascript">
for (yy=0; yy<10; yy++)
{
document.write("<tr>");
for (xx=0; xx<10; xx++)
{
if (mf.field[xx][yy] == "X")
{
mwl1 = "mwl.hide('#gamePage'); mwl.show('#loosePage');";
} else {
mwl1 = "mwl.toggleClass('#digDiggedField-" + xx + "-" + yy + "','ui-hide'); mwl.toggleClass('#digEmptyField-" + xx + "-" + yy + "','ui-hide');"
+ "mwl.toggleClass('#flagDiggedField-" + xx + "-" + yy + "','ui-hide'); mwl.toggleClass('#flagEmptyField-" + xx + "-" + yy + "','ui-hide');";
}
 
document.write("<td>");
document.write("<div class='gameField' id='digEmptyField-" + xx + "-" + yy + "' onclick=\"" + mwl1 + "\"></div>");
document.write("<div class='gameField ui-hide' id='digDiggedField-" + xx + "-" + yy + "'>" + mf.field[xx][yy] + "</div>");
document.write("<div class='gameField flaggedField ui-hide' id='digFlaggedField-" + xx + "-" + yy + "'>F</div>");
document.write("</td>");
}
document.write("</tr>");
}
</script>

If the field is flagged or already digged, it gets no onclick event. If the clicked field is a mine, the game shows the #loosePAge. If it is an empty field without a mine, it hides digEmptyField-xx-yy and flagEmptyField-xx-yy and shows digDiggedField-xx-yy and flagDiggedField-xx-yy . It is important to change the state in the dig and the flag tab. Otherwise it would still be possible to flag an already digged field.

2. The Counter

The game show you how many flags you need to set. At the beginning, the counter shows "10" and when ever you flag a field, it counts down. If you un-flag a field, it goes up. If you plant more than 10 flags, it becomes negative, showing you that some of your flags have to be wrong. The high number of needed DIVs is created in a loop.

<script type="text/javascript">
document.write('<div id="counter" class="inline">');
document.write('<div class="inline">10</div>');
for (cc=9; cc>-91; cc--)
{
document.write('<div class="ui-hide">' + cc + '</div>')
}
document.write('</div>');
</script>

The game fields in the flag view just use a simple MWL command to let the counter work:

onclick = "mwl.setGroupNext('#counter', 'inline', 'ui-hide', 'next');";
onclick = "mwl.setGroupNext('#counter', 'inline', 'ui-hide', 'prev');";

3. Have I won?

The biggest challenge was to check the conditions for victory without doing a round-trip. It is not only necessary that the use flagged all bombs, he also must not have flagged any bomb-free field. In addition, the "You have won!" page must not only be shown after flagging the last missing mine, it also has to show up after unflagging the last field which has no mine. To make this possible, I utilized mwl.iterateClass();. A pretty powerful function which increases or decreases a numbered class name until a limit is reached. It then even offers a callback function:

<script type="text/javascript">
for (yy=0; yy<10; yy++)
{
document.write("<tr>");
for (xx=0; xx<10; xx++)
{
[...]
if (mf.field[xx][yy] == "X")
{
mwl2 += "mwl.iterateClass('#gameCounter', 'clearedFieds', 'next', 101, false, 'mwl.hide(\\\'#gamePage\\\'); mwl.show(\\\'#winPage\\\');');";
mwl3 += "mwl.iterateClass('#gameCounter', 'clearedFieds', 'prev', 101, false, '');";
} else {
mwl2 += "mwl.iterateClass('#gameCounter', 'clearedFieds', 'prev', 101, false, '');";
mwl3 += "mwl.iterateClass('#gameCounter', 'clearedFieds', 'next', 101, false, 'mwl.hide(\\\'#gamePage\\\'); mwl.show(\\\'#winPage\\\');');";
}
 
document.write("<td>");
document.write("<div class='gameField' id='flagEmptyField-" + xx + "-" + yy + "' onclick=\"" + mwl1 + mwl2 + "\"></div>");
document.write("<div class='gameField ui-hide' id='flagDiggedField-" + xx + "-" + yy + "'>" + mf.field[xx][yy] + "</div>");
document.write("<div class='gameField flaggedField ui-hide' id='flagFlaggedField-" + xx + "-" + yy + "' onclick=\"" + mwl1 + mwl3 + "\">F</div>");
document.write("</td>");
}
document.write("</tr>");
}
</script>
 
<div id="gameCounter" class="clearedFieds-90">

Whenever a mine is flagged, the clearedFieds-yy will be increased, if the flag is removed, clearedFieds-yy will be decreased. Whenever an empty field is flagged, the clearedFieds-yy will be decreased, if the flag is removed, clearedFieds-yy will be increased. As soon as clearedFieds-yy reaches 100 (counting for the limit starts at 0), the callback function will show the #winPage.

Warning.pngWarning: If you document.write() an mwl..callback inside an mwl..function inside an onlick-event, the escaping starts to get complicated. There is a useful function in this discussion thread you could use.

Warning.pngWarning: The Nokia Browser cannot work with any class names which are not created upfront. This includes the auto-generated class names from mwl.iterateClass();. If you don't create them, your app will not work on device but in local and cloud preview.


Summary

The Nokia Web Tools, the Mobile Web Library and the Nokia Browser are highly capable tools which enable you to create very responsive apps for a very big audience. But you have to master MWL and you have to think sometimes outside of the box. Using MWL where ever you can, using JavaScript like a server-side scripting language and moving on-demand logic into CSS and the app start will reduce your server round-trips and increase the responsiveness of your application.

Gallery

148 page views in the last 30 days.
×