Friday, October 20, 2006

Lesson 4c: Resources and Display control

Lesson 4c: Resources and Display control

What we've discovered so far is that it is very easy to draw things on the screen and straightforward to update the screen using state transitions. However there lies a very subtle problem that affects the display on actual devices.
The problem is that visual elements are shown as soon as they are loaded. This does not sound like a problem, and on devices with fast filesystems, network connections, and graphics cards it isn't. The problem occurs when you attempt to run the code on a slower device with lots of network overhead or a slow graphics chip. Since the display only shows items as they are loaded, a page may not render fully because it is waiting for the requested images to arrive over the network.

Users may find this acceptable for some applications like web browsing or news browsing when the primary user interaction is reading text, but once the focus falls on displaying images, it becomes very difficult to provide a uniform, pleasant user experience if images are not displayed in a reasonable manner.

The most reasonable manner for most applications is to display all the images at once. This means that the program will load all the images before displaying them.

To manage resources, UJML provides the resource tag. Resources are defined by a URL, and they have the ability to give status notification via the event tag. Resources are also peculiar because they are only definable within state transitions. So by using a state variable it is possible to directly affect when a resource is loaded, and by effectively handling the load events it is possible to control the program flow.

When the state transition is active, the resources defined in it are loaded. When the state transition becomes inactive, the loaded resources are unloaded and any pending network requests are cancelled.

This lends itself to two straightforward applications. The first is very useful for applications that use a lot of images that provide the program's "look and feel". Putting all the images that will exist for the life of the application in a single state transition and setting it at the outset of the program will guarantee that those images will be immediately available any time they are needed. The second is to force all displayable items to be drawn simultaneously by not drawing the screen until all items have been loaded.

To create a "permanent resource partition", as I like to call it, all you need to do is define a new partition with a boolean state variable which is set to true in the script section of the partition definition. To complete the partition, a state transition is defined for the variable that contains all the permanent application resources.

<ujml>
   <partition>
      <state-variables>
         <state-var name="sLoadResources" type="boolean" />
      </state-variables>
      <script>
         sLoadResources = true;
      </script>
      <states>
         <state var="sLoadResources">
            <transition value="true">
               <resources>
                  <resource>
                     <url>http://example.com/a.jpg</url>
                  </resource>
                  <resource>
                     <url>http://example.com/b.jpg</url>
                  </resource>
               </resources>
            </transition>
         </state>
      </states>
   </partition>
</ujml>


In the above example code, two image resources are loaded when this partition is linked to the main application. The script is run immediately and sLoadResources is activated. Since sLoadResources is never set to false, the resources in the partition are available for the entire lifetime of the application, even from other partitions which may be loaded later.

That is the most straightforward usage of resources, however to gain the full benefit of the management technique, it is necessary to use the event tag to catch resource events.

There are two resource events. onResourceAvailable for successful loading, and onResourceError when an error occurred in the loading of the resource. These two events provide you with the tools necessary to regulate program flow.

The second technique which I mentioned above makes heavy use of resource events. The key is to tie each resource event to a counter which will signal when the program should proceed with processing.

<ujml>
   <partition>
      <state-variables>
         <state-var name="sLoad" type="boolean" />
         <state-var name="sDraw" type="boolean" />
      </state-variables>
      <variables>
         <var name="mLoadCnt" type="int" />
         <var name="mToLoad" type="int" />
      </variables>
      <functions>
         <function name="AllResourcesLoaded" type="void">
            <script>
               // All resources have been loaded, so paint the screen
               sDraw = true;
            </script>
         </function>
      </functions>
      <script>
         mLoadCnt = 0; // No resources loaded yet
         mToLoad = 2; // We are expecting two resources
         sLoad = true; // Begin the resource loading routine
      </script>
      <states>
         <state var="sLoad">
            <transition value="true">
               <resources>
                  <resource>
                     <url>http://example.com/a.jpg</url>
                     <event name="onResourceAvailable">
                        <script>
                           mLoadCnt++;
                           if (_eq(mLoadCnt, mToLoad))
                           {
                              AllResourcesLoaded();
                           }
                        </script>
                     </event>
                     <event name="onResourceError">
                        <script>
                           mToLoad--;
                           if (_eq(mLoadCnt, mToLoad))
                           {
                              AllResourcesLoaded();
                           }
                        </script>
                     </event>
                  </resource>
                  <resource>
                     <url>http://example.com/b.jpg</url>
                     <event name="onResourceAvailable">
                        <script>
                           mLoadCnt++;
                           if (_eq(mLoadCnt, mToLoad))
                           {
                              AllResourcesLoaded();
                           }
                        </script>
                     </event>
                     <event name="onResourceError">
                        <script>
                           mToLoad--;
                           if (_eq(mLoadCnt, mToLoad))
                           {
                              AllResourcesLoaded();
                           }
                        </script>
                     </event>
                  </resource>
               </resources>
            </transition>
         </state>
         <state var="sDraw">
            <display>
               <image>
                  <url>http://example.com/a.jpg</url>
                  <x>0</x>
                  <y>0</y>
                  <width>100</width>
                  <height>100</height>
               </image>
               <image>
                  <url>http://example.com/b.jpg</url>
                  <x>100</x>
                  <y>0</y>
                  <width>100</width>
                  <height>100</height>
               </image>
            </display>
         </state>
      </states>
   </partition>
</ujml>


Briefly, when the partition is loaded, it immediately begins loading the resources by setting the sLoad state variable to true. When each resource fires its success/error event, the corresponding script checks to see if all resources have been loaded, and if they have been, it sets sDraw to true to paint the two pictures side by side.

This technique can be extended to as many resources as you want, given the device's memory constraints. Also, if a function is provided that sets sLoad to false, the resources can be unloaded as well. It is even possible to create an array of boolean state variables to provide fine-grained loading and unloading of resources.

One key item to remember, though, is that as long as a resource is in use (by either the resource tag or a display tag) the resource is kept in memory. So in order to force a.jpg and b.jpg out of memory in the example above, it is necessary to set both sLoad and sDraw to false so that no state transition using the resource is active.

Resource do not necessarily have to be images, too. Sound files, even other UJBC files may be loaded using the resource tag.

The resource tag is the most important tool UJML provides for managing memory and providing users with a solid user experience. There are other even more fine-grained tools for resource management like _prefetch and _discard, but most of the heavy lifting will typically be handled using the resource tag.