Richard Evers, Editor

Development trends constantly change with the passage of time.

In the past, the focus was on execution speed and the reduction of system overhead.

The main focus today in non-wireless development seems to be on the ease and speed of development.

In today’s wireless world, current limitations are similar to past limitations, so the primary focus remains on execution speed and the reduction of system overhead.

While some wireless device limitations may lessen over time, speed and storage concerns will likely remain of top concern because of the adoption of Java to make development easier, faster and more full featured.

Topics within this section include:

Java

In theory, Java is often considered close to perfect as far as languages go. In reality, many problems have arisen related to a common view that Java is a universal language suitable for all forms of development.

This problem can be illustrated by the following examples:

In two cases, management made a decision to directly port cash cow C and C++ products to Java. They believed that creating a multi-platform product in Java would decrease long-term development costs and time to market.

The development teams bought into the concept completely and did their best to handle the port within the allotted time frame. Unfortunately, line-in-the-sand deadlines combined with crushing workloads working in a language that did not lend itself well to the port resulted in the creation of products that did not work as planned. The original applications were fast, reliable, easy to set up and non-consumptive, but the Java versions were not.

One of the companies made the mistake of converting their C and C++ code base directly into Java hoping that the end result would work well enough to release. It did not. The direct port resulted in a poor imitation of the original, but the company released the product anyway.

Both products failed to gain sustained acceptance after release. The development teams reacted by working harder than before to optimize the code, reduce bugs, and make adoption more palatable to the customer. Unfortunately, the damage had been done and new sales went flat.

It is possible to create full-featured applications in Java, as long as common sense is used throughout the design and development stages. It is important to understand the strengths and weaknesses of the language, the Virtual Machine, and the underlying hardware and network.

BlackBerry and Java

Research In Motion (RIM) chose the least traveled route to create their Java 2 Micro Edition (J2ME)-based BlackBerry wireless handheld. The JVM is optimized for the device and operating system, with native applications optimized for speed, overhead, and reliability. RIM also created a development environment and compiler that generates optimized code for the BlackBerry and extends the language with the BlackBerry API.

This was the correct, but not obvious, decision. RIM increased their development workforce and remained focused until they felt secure that their offering was world-class.

Do you Grok?

Wireless development is different than any other form of development. The limitations are similar to those encountered years ago when microcomputers were in their infancy. There are inherent limitations on the size of persistent storage and memory, available bandwidth, battery life, processor speed and screen size. In these early stages of life for the wireless industry the options of swapping in a faster CPU, increasing memory and storage, installing an accelerated video card, connecting a larger monitor, or accelerating transmission rates are not possible. If you write an application that makes excessive demands on Resources or causes instability, your application may not work properly, may not co-exist with other applications, and may alienate your potential client base.

The Profiler

A good way to isolate and address coding bottlenecks is to use the profiler that comes with RIM’s Java Development Environment. The profiler will display the percentage of time spent in each area of code up to the point of execution.

  • Summary View: Statistics are prepared showing the percentage of time that the Java VM has spent idle, running code and performing quick and full garbage collection.
  • Methods View: A module list is displayed sorted by profiled information or the number of times each item was executed.
  • Source View: Source lines are displayed to navigate through methods that call, or are called by, that method.

Overall, the profiler is a powerful tool that will help to quickly find and cure coding bottlenecks.

Memory Leaks

The Java Virtual Machine relies on a garbage collection task that locates and deletes objects that are no longer needed by an application. At each pass, the garbage collector starts at the root nodes where classes exist that persist for the duration of application’s life, then traverses each referenced node. During traversal, all actively referenced nodes are tracked, and all nodes that are no longer referenced are deleted, with associated memory passed back to the JVM.

If the high persistence class fails to clear its reference to the limited persistence class after dismissal, memory will not be reclaimed until the high persistence class goes out of scope. Memory can leak in situations where a high persistence class creates one, or more, limited persistence classes. For example, user-interface widgets (forms, controls, etc.) that are displayed then dismissed by users.

Common places to find the cause of memory leaks are in collection classes such as vectors, list and hash tables, especially if the class has been declared static and exists for the life of the application. Another common area is when a class is registered as an event listener, yet is never unregistered before the class goes out of scope.

RIM’s JDE provides a Memory Statistics tool and an Object tool that will display statistics on the number of objects and bytes that are used for object handles, RAM and flash memory. With memory statistics you set a few breakpoints, run your application, refresh and retain the memory statistics, run the application to the next breakpoint, and then refresh to compare memory statistics to the previous breakpoint.

Bandwidth and Latency

Java-based BlackBerry now supports operation on 3 different types of wireless networks: GPRS, CDMA (1X), and iDEN. The bandwidth on any given network can fluctuate from less than 5 kilobits/second to as high as 50 kbps. This variance in bandwidth would generally be acceptable if it were not for network latency. First round trip latency usually takes 5 seconds or more (depending on network traffic) to complete, while consecutive round trip attempts take, on average, 2 seconds or more.

Wireless applications should compensate by minimizing the amount of data transferred over the network, and hide latency delays from users. Utilizing the multiple-thread architecture of the BlackBerry is an effective way to improve both perceived and actual performance by handling more than a single task at the same time. For example, BlackBerry email is compressed then transmitted as a background thread to allow use of the unit while network activity is present. This provides the user with the perception of speed. Conversely, a web browser user must wait for a page to come up and does not expect to do anything else while waiting. A well designed wireless web site will reduce the impact of latency by minimizing the number of round trips a browser needs to fetch a page.

General Guidelines

The following are general guidelines that may be useful in day-to-day development.

  • Create a new thread for any lengthy operations such as network connections. Use background threads for listeners or other processes that you want to run in the background when the application starts.
  • When you use an inner class to hide one class inside another, but you do not need the inner class to reference the outer class object, declare the inner class static. This suppresses the creation of the reference to the outer class. The only time you should use a non-static inner class is when you need access to data in the outer class from within methods of the inner class. If you are using an inner class only for name scoping, make the inner class static.
  • When creating code libraries, ensure that you mark a class as final if you know it will never be extended. The presence of the final keyword enables the compiler to generate faster code.
  • In Java, a long is a 64-bit integer. Since the BlackBerry wireless handheld uses a 32-bit processor, operations run two to four times faster if you use an int instead of a long.
  • When defining static fields (also called class fields) of type String, you can increase program speed by using static variables (not final) instead of constants (final). The opposite is true for primitive data types, such as int.

    For example, you might create a String object:

    private static final String x = "example";

    For this static constant (denoted by the final keyword), a temporary String instance is created every time you use the constant. The compiler eliminates x and replaces it with the string "example" in the bytecode, so that the virtual machine (VM) has to perform a hash table lookup each time you reference x. In contrast, for a static variable (no final keyword), the String is created once. The VM performs the hash table lookup only when it initializes x, which makes access faster.

    Note: The BlackBerry JDE compiler automatically marks any classes as final that are not extended in an application .cod file.

    Note: It is acceptable to have public constants (that is, final fields), but you should always make variables private.

  • Use local variables wherever possible because access to local members is far more efficient than access to class members.
  • Interfaces produce larger, slower code. Avoid creating interfaces when creating API libraries unless you foresee multiple implementations of the API.
  • Use appropriate access modifiers for fields and methods when creating code libraries to reduce the size of the compiled code. Declare fields as private whenever possible, and use the default (package) access instead of public access (i.e. public and protected keywords).
  • Use or extend a class from the J2ME and BlackBerry APIs where possible, and use inheritance at every turn.
  • To increase speed, either inline the code in critical sections or reduce size by placing duplicate code in its own method.
  • Avoid using a lot of date objects to save space. Convert date values into longs.
  • Synchronized methods are twice as expensive as regular methods. If necessary, synchronize on methods rather than code blocks to slightly improve performance.
  • Try/catch exception handling is handled efficiently by the BlackBerry VM and does not introduce expensive overhead like other Java applications.
  • Avoid recursive calls in critical sections of code to increase speed. Use recursion in less critical sections to reduce code size.
  • Use native classes for machine performance that can’t be matched in Java. For example, arraycopy() is considerably faster than copying a large array in a loop.
  • Minimal savings can be found by using short names for file paths, classes, methods and instance variables. Unlike .jar format, applications created in .cod format are tightly compressed and obfuscated.
  • Some API classes are so full-featured that you’ll take a speed and size hit when you only have to use a subset of the functionality. If you run into this situation, profile your code using the API class, substitute your own simplified method, then profile again. The difference can be substantial in high traffic areas of code.
  • If you encounter performance issues with a method within an API class, override the method with a simplified, optimized version of your own.
  • Replace expensive Java data structures with simpler data structures to increase speed at a cost of code complexity. For example, multi dimension arrays are, by far, more expensive than single dimension arrays because of the extra indirection.
  • Switches use fast or slow search algorithms depending on how close the switched values are. A (fast) direct lookup is used for close values where a (slow) table search is used for disparate values.
  • Methods can only be inlined when they are final, private or static, and lack local variables. If possible, rewrite high traffic methods to be final sans local variables for inlining.
  • Reuse objects wherever possible. It’s expensive to create a new object and eventually leads to garbage collection. In comparison, ‘Object obj = new Object()’ is 43 times more expensive than declaring ‘int i=0’. To put the BlackBerry VM in a more favorable light, it is upwards of 3,000 times more expensive to create an object within a conventional Applet than ‘int i=0’.
  • Avoid using immutable objects to save on garbage collection from discarded objects left behind after the creation of a new, altered immutable objects.

    The following code snippet demonstrates how to create garbage with an immutable String:

    public String FibonacciString1(int start, int end){
    String s = new String();
    for (int i = start; i <= end; i++){
    s = s + Fibonacci1(i);
    s = s + " ";
    }
    return s;
    }

    An immutable String called ‘s’ is created outside of the loop. Within the loop, ‘s’ is discarded and created, then discarded and created again, as many times as necessary thus leaving garbage on the heap at each pass. A less consumptive approach would be to create a mutable StringBuffer then append data into the buffer:

    public String FibonacciString2(int start, int end){
    StringBuffer s = new StringBuffer();
    for (int i = start; i <= end; i++){
    s.append(Fibonacci2(i));
    s.append(" ");
    }
    return s.toString();
    }
  • If your application relies on images that are in Portable Network Graphics (.png) format, make sure to export them in the most compressed form possible in the same format. Some imaging packages save .png images in a format that can be considerably reduced through supported compression techniques. As an example, I created a 32 x 32 bit, 24-bit color image using such a product. The size was 29,582 bytes in original form, and 1,050 bytes in compressed form.
  • Reduce the number of custom classes in your application as much as possible. Each class definition increases the application size. Combine related methods beneath a single class to conserve space.
  • Use clipping to reduce the amount of work done in repaint().
  • Use high-level graphic primitives where possible to avoid repeat calls to lower level primitives.

In Closing ...

Take pride in creating small, fast and bug free wireless applications. Oppose unrealistic deadlines at every turn. Slow, bloated and buggy applications don’t make the grade in the wireless world.

Enjoy the wireless experience!