浏览 5226 次
锁定老帖子 主题:A J2ME FAQ
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-03-11   最后修改:2010-08-15
J2ME technologies
What are the main differences between programming for a J2ME JVM, and programming for a J2SE JVM?
Most obviously, the J2ME programmer has access only to a reduced range of basic API classes. There is no built-in API support for file handling, RMI, JDBC, JNDI, multimedia (but this is changing), or the AWT/Swing user interface models. Some of this functionality is replaced by J2ME-specific classes. For example, the Java package javax.microedition.io.lcdui provides a class library for a simple widget-based user interface. javax.microedition.io provides an implementation of the HTTP protocol.
      As well as these API and library differences, there are subtle differences in how J2ME JVMs work. For example, there is no JVM support for floating-point arithmetic (not even a double data type or Double class). Garbage collection is simplified - there are no soft references (yet), and finalize() methods are never called.
      Of course, the limited memory availability in a J2ME device will require much more careful use of instances than J2SE developers will be used to. It will be necessary to re-use objects rather than creating new ones, and the developer will have to help the garbage collector more explicitly by nulling unwanted object references.
What is a `configuration'?
In J2ME jargon, a `configuration' is a set of hardware functionality made available to applications through a JVM. J2ME defines two basic configurations: CDC and CLDC (see below).
What are CDC and CLDC?
Connected Device Configuration and Connected Limited Device Configuration. These are the specifications for the basic JVMs that a J2ME device must support. CDC is for bigger devices, such as set-top boxes and PDAs. These will typically have more memory and more robust network connectivity. CLDC is for smaller devices such as mobile phones and pagers. CDC and CLDC are not really APIs (although CLDC does specify a basic API), but run-time environments. The APIs are exposed to the developer through a set of `profiles' (see below). In practice, there appears to be much more interest in CLDC, as most J2ME developers are working on mobile phone and low-end PDA applications. CDC implementations are available for upmarket PDAs like Compaq's iPAQ range.
What is a profile?
`Profile' is J2ME jargon for the API exposed by the J2ME implementation. The situation is complicated by the fact that CLDC also defines an API, but in reality this API cannot be separated from the API exposed by the profile. There are four basic profiles defined by the J2ME specifications, of which only one - MIDP - is of much interest at the moment. The MIDP profile is based on the CLDC JVM. The others, Foundation, Basis, and Personal, are based on CDC.
What is MIDP?
MIDP (Mobile Information Device Profile) is the API exposed by small mobile devices with graphical displays. MIDP is supported by most mobile phones, pagers, and low-end PDAs. It is not by any means the only J2ME profile (API set) defined, but it is the most widely supported.
What is MIDP-NG?
MIDP `next generation', now re-branded more modestly as `MIDP2.0'.
What's new in MIDP2.0?
In brief...

    * The user interface has been extended to support such things as pop-up windows, and active strings and images
    * Greater control of screen layout, including the ability to set constraints on the sizes of items on a form
    * Custom form items: applications are no longer limited to the basic set of form items, but can implement new types in Java
    * Support for simple audio playback (MIDP2.0 devices must support a small subset of the features in the MMAPI. Vendors can support MMAPI as well, of course)
    * Sprite API to simplify screen management in graphical applications, particularly games
    * Support for HTTPS, raw TCP/IP and UDP, and serial ports
    * Application settings now provide for a configuration by which MIDlets can be activated by external events. This is is called the `push architecture' in the documentation.
    * A MIDP2.0 device must be able to retrieve MIDlet suites by over-the-air (OTA) access. In fact, most MIDP1.0 devices support OTA provisioning, but it was not compulsory.
    * Support for `signed MIDlets'. Like a Java applet, a MIDlet can now be signed, that is have appended to it an encrypted hash of its contents. The signature provides a robust method for determining the identity of the distributor of the MIDlet. The operator of the device can use that knowledge to choose to allow the MIDlet greater access to the device's runtime environment.

Must a MIDP2.0 device support the multimedia API and wireless messaging API?
These are defined as optional packages, and therefore need not be implemented by MIDP2.0 devices. However, MIDP2.0 devices must support basic wave audio, as defined in the MIDP2.0 specification.
What is LCDUI?
The user interface model user by MIDP. It is not clear what `LCD' actually stands for. The balance of opinion is that the letters `LCD' has the same meaning that they do in CLDC, but it isn't clear why they aren't in the same order. I prefer the other widely-used interpretation: lowest common denominator. Try it, and you'll see why. In the MIDP documentation, you'll see reference to the `high-level user interface' and the `low-level user interface'. I don't really like these terms, because they sort of give the impression that the high-level user interface is an abstraction of the low-level user interface, or that one is based on the other. However, this isn't true at all in MIDP. The `high-level user interface' is the programming model based on forms and widgets, while the low-level user interface is the Canvas class and the graphics primitives. I prefer the terms `form-based' and `canvas-based'.
What are the Foundation, Basis, and Personal profiles?
These are APIs based on the CDC JVM. The Foundation profile (API) provides a basic Java class library, but no graphical user interface. The Basis profile provides, in addition, a lightweight graphical user interface based on the `xlet' model. The Personal profile is the most complex, and supports the full AWT graphical programming model. Programming graphical applications for the Personal profile is therefore much like programming ordinary workstation applications in AWT. The Personal profile also supports Java applets. All three CDC profiles can be extended to support RMI and JDBC, but vendors are not required to support them.
How does JavaCard fit into all this?
JavaCard is a JVM optimised for running on embedded systems like security cards. It has its own specification and programming model, and is not really connected with CDC, CLDC, or MIDP.
How does PersonalJava fit into all this?
PersonalJava was an early attempt at a JVM for small devices. It is not related to the current J2ME effort. However, the Personal Profile on the CDC platform provides a similar level of functionality to PersonalJava.
Is MIDP a layer on top of CLDC?
Sort of. However, it is wrong to assume that MIDP is, or even can be, implemented using only the CLDC API. To the developer, the MIDP and CLDC APIs exist side-by-side. For example, the MIDP API allows screen management, but there is no CLDC support for screens, even at the pixel level. MIDP2.0 offers support for networking operations such as raw TCP/IP sockets, but CLDC only offers HTTP support. It would be strange if a higher-level abstraction offered lower-level functionality than the thing it abstracts. In practice, developers working with CLDC devices are actually working with MIDP, and there are no other standards-based user interface models for CLDC. As a result, it is not really productive to try to distinguish the MIDP layer from the CLDC layer.
What is the K Virtual Machine?
K (kilobyte) Virtual Machine (KVM) is a reference implementation of a CLDC JVM from Sun Microsystems. KVM forms the basis for a number of commercial J2ME devices, and also of the MIDP emulator in Sun's Wireless Toolkit product. However, it is a popular misconception that KVM and CLDC are synonymous. KVM is a piece of code, CLDC is a specification. CLDC implementation exist that are not based on KVM.
What versions exist of MIDP and CLDC?
At the time of writing, there were two versions of CLDC: 1.0 and 1.1. Version 1.1 is a significant enhancement over 1.0, and includes more sophisticated memory management and floating point support. The latest version of MIDP is 2.0 (also called `MIDP-NG') which is, again, a significant enhancement over 1.0. Unfortunately, MIDP2.0 support does not require CLDC1.1, only CLDC1.0. So MIDP2.0 applications cannot take advantage of the new CLDC1.1 features, or at least cannot do so in a portable way. This situation is likely to persist, because the hardware requirements for supporting MIDP2.0 are actually somewhat less than for supporting CLDC1.1. As a result, there are MIDP2.0 mobile phones that could probably not support CLDC1.1.
      At the time of writing (Spring 1993) there are relatively few MIDP2.0 devices on the market, and most developers are still targeting MIDP1.0.
What are `optional packages'?
These are extensions to the basic profiles defined by specification, but not mandatory. Some devices support them, most don't. They are in various states of development.

    * The MMAPI (multimedia API) profile provides support for streaming video and audio
    * The WMA (wireless messaging API) provides support for, er.., wireless message (SMS, etc)
    * The PDA profile provides access to the standard application data manipulated by a PDA device, such as calendars and address books.
    * The FCA (File Access API) profile provides facilities to read and write files on devices that have the concept of a file

Are J2SE and J2ME JVMs bytecode compatible?
In theory, all J2SE JVMs are bytecode compatible. For example, a class compiled with one vendor's compiler tools should behave identically on another vendors JVM. However, classes compiled with a J2SE compiler will not necessarily work on a CLDC JVM. The reason for this is that CLDC is necessarily less sophisticated than a full-scale JVM, and cannot do the iterative bytecode verification that a proper JVM has to do. As a result, CLDC developers typically compile Java using an ordinary Java compiler, then convert the standard bytecode it into CLDC-compatible bytecode using one of the various tools that are available (see below).
How large can a CLDC/MIDP application be?
There are main limits to consider: the size of the application's bytecode, and the memory it uses at run time. The latter may comprise both volatile (stack, heap) elements and non-volatile (record store) elements, and is harder to estimate.
      The bytecode limit depends on two things: the limit imposed by the hardware device, which will almost always be somewhat less than its total non-volatile memory capacity, and the limit imposed by the provisioning mechanism. If you are downloading the application from a PC by a cable or infra-red link, this limit is likely to be unproblematic. For over-the-air provisioning (see below) you may run into limits imposed by the network operators. Ultimately, an OTA download will be implemented by an HTTP response, and it simplifies the network operator's equipment if it can be assumed that every HTTP response will fit into a single data packet on whatever the underlying carrier is. I believe (but I am happy to be corrected) that the worst culprits limit the download size to 8 kB. That's right, eight kilobytes. In practice, most MIDP devices, even mobile phones, can cope with applications whose bytecode sizes are about 32 kB without too much stress, but you may have to upload them using a cable. But for maximum portability, bear in mind that the MIDP specification only requires that a device provide a total of 8 kB of non-volatile storage.
      At runtime, the application will consume stack and heap space. These will usually be drawn from the device's volatile memory, which need by no larger than 32 kB to comply with the specification. 128 kB is more usual. An application may also use non-volatile storage, or which only 8 kB need be provided (in addition to that required by CLDC itself). Again, 128 kB is more usual.
How can I reduce the memory footprint of my application?
First consider how much effort you want to expend on this. Modern MIDP/CLDC devices have far more memory than the original specifications envisaged. If you must reduce memory size, bear in mind that all the following techniques will reduce the readability and manageability of your code. In no particular order...

    * Don't create separate classes to act as event listeners - use a class that already exists. For example, use your MIDlet's main class as the event listener for form events, rather than creating a separate class. Remember that even the most trivial, anonymous inner class requires 200 bytes of bytecode, plus some state information for each instance.
    * Where possible, use ordinary named classes, not inner classes. It is conventional in AWT programming to use inner classes to act as event listeners. The state information required to support an instance of an inner class can be surprisingly extensive, because of the way that inner classes are managed by the JVM.
    * Don't subclass the built-in classes unless you have to. For example, you could create a subclass of Form to serve as your applications main user interface, but you could also use an ordinary Form object, and drive it from the MIDlet's main class. Subclassing Form is more `OO', but imposes adds a few hundred bytes to the footprint. Every class you subclass has the same effect.
    * Don't subclass your own classes unless you have to. The flatter your class hierarchy, the few the classes; the fewer the classes, the smaller the bytecode.
    * Don't put your classes into packages. J2ME applications are self-contained, so there is no prospect for name clashes between applications. The packages names have to be stored in the bytecode, on every occasion that a class is defined or referenced. A better solution than de-packaging all your code is to use a software tool to do it. Bytecode obfuscators do this as part of their normal operation.
    * Remember that array initialisations translate into lots of repeated bytecode operations. So initializing an array of month names like this:

      String[] months = {"January", "February"...

      generates a surprising amount of bytecode. If you have very large arrays of strings or numbers, it generates less bytecode to define it in a long, delimited String, and split it into individual values in a loop. This may seem mad, but it's true.
    * If you have to support multiple locales, plan on distributing different versions of the application for different locales, rather than supporting multiple locales in the same application. There's no point providing a load of French and Japanese text if the user wants to see only Italian.
    * If an application creates a Java instance, and knows that it is no longer required, it should re-use if with different data, or set it to null. Re-use is the preferred option, where this is practicable, but setting to null will help the garbage collector determine which objects are out of scope.

What Java version does a J2ME device support?
When developers ask this question, they are usually concerned with the range of API support available. However, as has been explained, J2ME presents a very limited API, which does not correspond to any of the standard Sun JVMs. As a result, the correct answer to this question may be misleading. Strictly speaking, J2ME devices have to support the same language syntax and semantics as Sun JDK 1.3, with the exceptions described above - no floating point support, limited garbage collection, etc. However, developers should not assume that any of the standard JDK 1.3 classes are available, but should instead refer o the API documentation for the configuration and profile they are targeting.
J2ME development procedures and tools
How do I compile Java source code for a MIDP application?
The usual method is as follows:

    * Compile using an ordinary Java compiler
    * Convert the classes to CLDC format
    * Create a manifest file containing configuration information
    * Package the classes and manifest into a JAR file, including any other required files such as images and sounds

The JAR file can then be deployed to the device, or run on an emulator (see below).

When compiling the source code, it is helpful to tell the compiler not to compile against the standard J2SE class library, because most of it doesn't exist in CLDC. Instead, the compiler should refer only to CLDC and MIDP classes. You should be able to get JAR files of these classes from vendors of mobile devices, or use the archives included with Sun's Wireless Toolkit. With the Sun JDK, specify the classpath like this:

javac -bootclasspath [path_to_CLDC_jar]:[path_to_MIDP_jar] -g:none ...

The switch -g:none tells the compiler not to include any debugging information. This is important with mobile applications, because there is often a limit to the size of the application that can be transferred to the device.

After compiling the source code to .class files, the next step is to convert the bytecode in the .class files to CLDC format. This is not a trivial job. The CLDC JVM assumes that each method in each class that has any kind of looping or branching operation in it is supplied with a `stack map', which is essentially a table of all the possible flows of execution through the method. With a standard JVM, this analysis of flow is carried out by the bytecode verifier as an iterative process when each class is loaded. Since a CLDC JVM cannot be guaranteed to have the CPU resources to undertake this task, or at least undertake it in a reasonable time, the information must be inserted into the bytecode before deployment. This process is called preverification. Sun's Wireless Toolkit includes a preverifier, but there are other preverifier implementations in the works (see below for why).

After preverification, the modified classes are archived along with a manifest file, ready for deployment. The manifest contains information about the MIDlet classes included in the JAR, and the versions of MIDP and CLDC the application expects.

For maximum portability, the developer should provide, in addition to the JAR file, a `JAD' file. This is simply a text file containing the same information as the manifest in the JAR.
How do I deploy a MIDP application?
This varies from device to device. Vendors generally provide PC software to upload MIDP applications to their devices, either by cable or infra-red link, and it may also be possible to download from a Web server. With WAP phones and the like, it may be possible to download the application through data connection. In J2ME jargon, this is called over-the-air (OTA) provisioning. Some devices expect to be able to download a JAD file before retrieving the JAR file itself, so if you are providing MIDP application from a WAP site, you should link to JAD files, not JAR files. The reason for this is that data connection speeds from mobile phones are not terribly fast, and the phone should not have to go to the trouble of downloading a large JAR file, only to find out that it can't support the application because it expects the wrong MIDP version.
What does the Wireless Toolkit do?
Sun's wireless toolkit (WTK) automates the development steps described above - compilation, preverification, and packaging. It provides a nice graphical user interface for doing this as a one-click operation. It also includes a MIDP emulator for testing applications. The emulator is configurable to the extend that you can change the keypad layout and screen size. This is very useful if you are targeting a particular device or range of devices.
How is developing for an emulator different from developing for a hardware device?
The emulator supplied with the WTK is a fairly realistic simulation of a MIDP device, down to the telephone-style keypad and display. However, the emulator does run on a real JVM, and may not have the memory constraints of a real hardware device. It may also allow Java operation that would not work at all on a hardware device, such as floating-point arithmetic. One notable omission in the WTK emulator is the ability to simulate the pausing of a MIDlet. When a MIDlet is loaded, it remains in the active state (see below) until it is terminated. As a result, it is difficult to test that your startApp() and pauseApp() methods are properly complementary to each other. The developer should therefore take particular care to code the resource allocation and deallocation strategy properly.
Why is Sun's WTK only officially supported on Windows?
Because the preverifier tool it includes was originally developed for Windows (see below), and has only recently been ported to Unix.
What is wrong with the WTK's preverifier?
There is nothing wrong with it in so far as it does the job. The problem is that it was written in C, for the Microsoft Windows platform. It has since been ported to Solaris and Linux, but Sun does not support these new versions (yet). Problems with porting the preverifier limit the platforms on which the WTK will run. What is clearly required is a preverifier written in Java, so that the whole J2ME development process can be platform neutral. There are a number of open-source and commercial projects underway to develop such support.
What is the file emptyapi.zip that is supplied with the WTK?
It is an archive containing all the same classes and methods as the real MIDP/CLDC APIs, but all empty. This is provided as a workaround for a limitation in the RetroGuard bytecode obfuscator. It appears that RetroGuard gets hung up when any class is defined to contain native methods. When using RetroGuard, the trick is to ensure that emptyapi.zip is the first thing in its class search path, so it does get upset about the native methods in the real API.
What other J2ME tools are available?
Modern versions of the Ant build tool integrate the Sun preverifier, at least on platforms that support it. This makes it straightforward to use Ant to automate the development of J2ME applications. The SunONE Studio IDE now has complete IDE support for J2ME development, including compilation, preverification, emulation, and source-level debugging. I understand that there is a J2ME-specific version of JBuilder as well, although I've not used it. Most mobile phone vendors provide their own J2ME development tools and emulators.
MIDP/CLDC programming
What is a MIDlet?
A MIDlet is a Java class that forms the interface between the application and the operating system in a MIDP device. The MIDP platform provides a basic, do-nothing MIDlet class javax.microedition.midlet.MIDlet. Application developers extend this class, overriding the appropriate base-class methods. A J2ME application must include at least one MIDlet, in addition to any supporting classes the application needs. All are packaged into a JAR file for distribution.
What is a the `Hello world' of MIDP?
As far as I know, the following is the simplest MIDP application that can display a message on a graphical display.

import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class HelloWorld extends MIDlet
{
public void startApp()
  {
  Display d = Display.getDisplay(this);
  Form f = new Form ("Hello World");
  d.setCurrent(f);
  }

public void destroyApp(boolean force) { }
public void pauseApp() { }
}

You could also implement `Hello world' by creating a custom Canvas class and drawing the text using graphics primitives.
What is a MIDlet `suite'?
A group of MIDlets distributed in a single JAR file. The significance of a `suite' is that MIDlets in the same suite have greater ability to share external resources (e.g., non-volatile data) than separate MIDlets.
What is the MIDlet lifecycle?
A MIDlet is either in the active state or the paused state. When it is first loaded by the MIDP runtime environment, it will be in the paused state. The runtime will generally call the method startApp() to signal that the MIDlet should now activate itself. Whenever the application is idle, the runtime can, in principle, put the MIDlet back into the paused state. When it is paused, the application will not receive user interface events. On a mobile phone, for example, the runtime may pause a MIDlet if an incoming telephone call is detected. The runtime will call pauseApp() to indicate that it has done this. The developer should use startApp() and pauseApp to allocate and release resources that the application needs whilst it is running.
      When the operator is finished with the MIDlet, the runtime will destroy it. Before it does this, it calls the method destroyApp() method. The MIDlet must deallocate resources here as it does when it enters the paused state. However, this deallocation should not be done carelessly, as a MIDlet can be destroyed when it is in the paused state, as well as the active state. In the MIDP documents, `destroyed' is described as being one of the states that the MIDlet may be in. This terminology raises a philosophical problem: if something is destroyed, it no longer exists; how can something that doesn't exist have a state? Apart from the philosophical issue, `destroyed' is not really a state, because the MIDlet cannot leave the destroyed state and enter any other state.
How can a MIDlet terminate itself?
It calls its own notifyDestroyed() method. The runtime then cleans up the MIDlet. Note that the runtime does not call destroyApp() when the application terminates itself, only when the runtime terminates it. As a result, the application typically calls its own destroyApp() method before calling notifyDestroyed().
How can a MIDlet put itself into the paused state when it is active?
It calls its own notifyPaused() method. As an application can always pause itself (it does not require the permission of the MIDP runtime to do this), it must deallocate any allocated resources before pausing. The runtime will not call pauseApp().
How can a MIDlet put itself into the active state when it is paused?
The simple answer is that it calls its own resumeRequest() method. However, this raises the question: how does the MIDlet get to run any code when it is in the paused state? If it is paused, one would imagine that it does not have the ability to run the resumeRequest() method. However, an application in the paused state is not forbidden to run any code; it just doesn't get user interface events. If the application has spawned another thread, for example, that thread may continue to run even when the application is paused. More commonly, an application may set an asynchronous timer to wake itself up after a certain length of time.
      A request to resume is exactly that: a request. The runtime need not honour that request. Contrast this with the ability of the MIDlet to pause or terminate itself - no permission is required to do these things. As a result, a MIDlet should not reallocate its resources before or after calling resumeRequest(). Instead, it should wait for the runtime to call startApp().
How does the MIDP user interface model compare to AWT?
Conceptually it is very similar. Each user interface widget is an instance of a class. Some widgets can contain other widgets, so complex displays can be built (within the constraints of the screen size). Some widgets generate events, which can be handled by the application. The display manager is responsible for placing widgets, not the application. However, MIDP is much simpler than AWT, and immeasurably simpler than Swing. There is no explicit menu support (where would the menu go?), nor can the developer (until recently) create entirely new user interface widgets. There is no windowing: although one screen can be raised on top of another, the top screen obscures the bottom one completely.
Is the MIDP user interface model confusing and inconsistent?
Seen from the context of AWT or Swing, the MIDP user interface model certainly appears to lack internal consistency, and not comply with even the mist rudimentary objected-oriented design principles. Here some examples...
      Consider the difference between the TextField class and the TextBox class. Both are areas of text, editable by the operator. Both look and behave much the same. The TextField is intended to be embedded within a form, along with other user interface elements, while the TextBox occupies the whole display in its own right. Both have similar, but not identical, methods for manipulating and retrieving the contents. However, despite their similarity these two objects do not extend a common base class, nor implement a common interface. Compare this with the List and ChoiceGroup classes. A List is a list-selection user interface element that occupies the whole display (as a TextBox does), while ChoiceGroup is a similar element intended to be embedded in a form (like TextField). This time the two classes do implement a common interface. There appears to be no logical explanation for the differences between these two different pairs of classes. It is also not obvious why there needs to be a distinction between TextBox and TextField in the first place. If there were no TextBox class, a similar effect could be obtained by creating a form containing only a single TextField. A similar argument can be advanced in respect of List. In any case, why is it that text and choice boxes can be screens in their own right, while other elements that can exist on a form (such as images, gauges, and plain strings) cannot?
      The classes List, Form, Alert and TextBox as subclasses of Screen. The other thing that can form a screen in its own right is a Canvas, but Canvas is not a subclass of Screen, it is a subclass of Displayable, which is also the base class for Screen. In other words, Canvas is at the same position in the class hierarchy as the abstract base class of all the other screen types. It is not clear what, say, List and Alert have in common that Canvas does not have. A possible answer is that List and Alert cannot receive raw keyboard input, while Canvas can. But there is no logical reason that I can suggest why Screen subclasses should not be able to process raw keyboard input if they want to.
      One more example: form items can be subclassed to change their behaviour. However, many of the important methods in these classes cannot be overridden by subclasses, so subclassing is of limited value. If the intention was that these classes should not be subclassed at all, they could have been declared as final. However, they aren't defined as final, and so there must be circumstances in which it is sensible to subclass them. But the subclasses can only modify the behaviour of the superclasses in very limited ways; in particular, the subclasses cannot override paint() and thereby change how the items are displayed. There appears to be no reason why form items can be subclassed at all, if the main reason for needing to subclass them is prohibited.
      What are we to make of all this? The MIDP user interface model is the ultimate triumph of practicality over design elegance. MIDP applications have to run in very little memory, and an instance of a class that has fewer base classes in its parentage has a smaller memory footprint than an instance that has more. At the same time, each additional class in the API set occupies a piece of the device's firmware. As a result, the MIDP model provides separate classes for user interface elements that are likely - in the kinds of applications that MIDP supports - to form complete screens. It does not provide such classes for elements that are unlikely to form a complete screen. For example, there is a whole-screen TextBox object, but no whole-screen static string or image. There is no logical reason for this, but these additional classes would take up more firmware capacity. Similarly, while a TextBox could be simulated by a form containing a TextField, the latter would require more memory at run time.
      It is not self-evident, I think, that these design choices represent the best compromise between logicality and practicality. However, there is at least an arguable case that they do, or at least that the did. If you read the answer to the question above ``How do I reduce the memory footprint of my application?'', you'll notice that the MIDP API designers have done nothing more than the answer to that question recommends. The troubling question at present is whether an API based on such fanatical memory saving is appropriate in modern mobile devices.
Are there AWT implementations for CLDC that can be used instead of the MIDP user interface model?
There are, for certain devices. See, for example, kAWT. To provide a decent implementation of AWT, low-level access to the hardware is required. There is thus no fully portable AWT implementation. It's worth bearing in mind that the decision not to use AWT as the user interface model for MIDP was not taken lightly; a full implementation of AWT is likely to have at least a 100 kB memory footprint. You might get away with this on a PDA, but it's doubtful that you'll be able to support mobile phones.
Are there AWT implementations that run on top of MIDP?
Yes, there are AWT compatibility libraries for MIDP. However, they generally only provide AWT-style programming support on MIDP devices, not full AWT functionality. Consider the problem of implementing an AWT BorderLayout, for example. There are no MIDP API calls that will produce this style of layout, so no AWT compatibility library that runs on top of MIDP can produce a BorderLayout. Moreover, even where an AWT component can be implemented in a way that is logically equivalent in MIDP, it may not look the same. For example, a compatibility library may implement an AWT MenuBar using the nearest MIDP equivalent, but it won't look like a menu bar in an AWT application running on a workstation, or even in an applet. Bear in mind that an AWT compatibility library is likely to add about 100 kB to the size of your application.
How difficult is it to port AWT applications to MIDP?
For an application of any complexity, this is fairly difficult. The event handling model and user interface class hierarchy are very different. There are many things that AWT can do that MIDP can't do. Using an AWT compatibility library will probably make the job a bit easier, at the expense of increasing the application size quite a lot.
Are there other user interface models apart from MIDP's that will run on CLDC?
Sure, but they are all vendor specific. The good thing about using a vendor-specific user interface model like Motorola's `LWT' is that it is implemented in firmware, so it doesn't bloat the application in the way that using an AWT compatibility library would. The bad that about using a vendor-specific user interface model is that it is vendor-specific.
How does a MIDP application deal with the wide diversity of input devices?
A MIDP device may have as few as four keys, but some have full QWERTY keyboards or pointers. This presents a challenge for the developer. MIDP supports two different input strategies: form-based input (typically used when a Form object is the current displayable), and raw keyboard input (typically used when a Canvas object is the current displayable).
      With form-based input, the methods by which data is entered into the form elements, and the method of navigation between form elements, are all vendor-specific. For example, if the operator has a mobile phone that requires multiple keypresses to enter alphanumeric data, the MIDP application should not have to worry about this. The device will arrange that the data entered - however this is achieved by the operator - is presented to the application as ordinary Java Strings. Similarly, if the device has a four-way arrow keypad, the MIDP implementation may map arrow operations to navigation operations. The way it does this will be different if the device has only a two-way arrow keypad, or if it has no navigation controls at all. For the developer, all that is really important is to create the form elements, and process their contents when the form is submitted.
      With raw-keyboard input, the application receives raw key codes as integers. This method of input usually requires considerable developer effort to make portable, as there is no correspondence between key codes and, for example, ASCII character values. However, if the application only requires a small number of keys, you may be able to use the `game keys' mechanism. Here the device vendor allocates certain keys to correspond to typical gaming actions: up, down, left, right, fire, etc. If the device has an arrow keypad, the `up' game action might be mapped to the `up' arrow key, for example. If the device has only a number keypad, a typical approach is to map game actions to number keys. This is all vendor specific; the device vendor should indicate to the user which keys form the standard game actions on that particular device. The vendor's MIDP implementation must then provide an method Canvas.getGameAction() that translates vendor-specific key codes into vendor-neutral game actions.
      What all this means in practice is that if your application accepts raw keyboard input, but is restricted to handling a few stereotypical keyboard actions, you can do this in a portable way. More sophisticated raw keyboard processing will require a fair amount of work if multiple devices must be supported.
      Whether the application uses form-based input or raw keyboard input, it can still take advantage of MIDP's rudimentary menu facility. The application adds `commands' (instances of the Command class) to the displayable object, and these are mapped to either keys or a menu by the device. For example, if you add two commands to a displayable, and the device has two action buttons under the screen, then it may map one command to each action button. If there are more than a few commands in the `menu', then the device may display a real menu when the operator presses a particular key. The exact implementation details are vendor-specific. Because the user interface of a MIDP device is generally rather impoverished, it helps the user if selecting common or popular operations can be accomplished in a minimum of user interface operations. As a result, when the application adds commands to a displayable, it gives each one a priority. The MIDP implementation can then try to map high-priority commands to more prominent buttons, and relegate low-priority commands to a menu.
How do I create and display a form-based user interface?
Create an instance of the Form class. Create instances of the objects you want on the form. Append the objects to the form. Get the current display object, and call its setCurrent() method to raise the form on the display. In practice, you will have to add at least one Command object to the form, or the user won't have any way to submit the form.
How do I create and display a user interface that isn't form-based?
Create an instance of a class that extends the Canvas class. This class must override the paint() method, and draw its display using graphics primitives. Get the current display object, and call its setCurrent() method to raise the canvas on the display. You can process raw keyboard input by overriding the keyPressed() method, or by adding commands to the canvas, or both.
Can I mix form-based and canvas-based displays on the same screen?
In general, no. You can't add a Canvas object to a Form, nor vice versa. MIDP is not that logical. Nor can you easily create a custom form element and draw it yourself in MIDP1.0 (see below). What you can do, if you have the time and enthusiasm, is to create your own entirely new user interface model using objects that draw themselves on a Canvas object. You could even handle pointer events if you are targeting a platform that supports points, and provide the missing functionality in MIDP. However, this is not a trivial task.
Can I create custom form elements (other than TextField, etc)?
In MIDP1.0, tricky. There is no provision for this in the API, and although you can create subclasses of the standard form items like TextField, there is no public paint() method that you can override. You can override the (package access) callPaint() method, but only be creating a subclass in the javax.microedition.lcdui package, a highly dubious practice. The problem is that the reason methods are given package access, rather than protected access, is to discourage developers creating subclasses. It is part of the general contract of Java development that package-private methods in any class can change from one release of software to the next, and clients of that class have no grounds for complaint if it does.

This unsatisfactory state of affairs is fixed in MIDP2.0. You can create custom form items in addition to the basic built-in items. A custom form item behaves much like a Canvas embedded in a form.
What is an Alert?
An alert is a kind of pre-defined form, which has a caption and a message. It is intended for displaying error or warning messages during an application. You can put up an alert by creating an instance of the Alert class and passing it to Display.setCurrent(), just as you would to display a canvas or form. Despite the similarity between an Alert and a form, Alert is not a subclass of Form (sigh). An Alert can be self-lowering, that is, it can take itself off the display after a certain time elapses.
Can I do console-style I/O in a MIDlet?
In general, no. Such operations may be supported by vendors, but they aren't defined in the MIDP specification. You can do console-style output by creating a TextBox object and appending text to it line-by-line, but this is not the sort of application that MIDP was intended for. The MIDP implementation is not required to do anything useful in response to a call to System.out.println(), and generally won't (see next question).
Where does System.out.println() go?
In an emulator, System.out.println() usually outputs to the emulator console or log. In a real device, it may just get junked. However, some devices echo standard output and standard error to their serial ports, so if you plug in a terminal or terminal emulator, you might be able to see the output.
What image file formats does MIDP support?
PNG V1.0. Within the PNG format, only compression method `0' is supported. Interlaced PNGs are supported, but there is no advantage to using interlacing with MIDP applications, as the whole image is read and rendered before being transferred to the display. In fact, on most devices doing an image read in one thread suspends all other threads, so you can't even read an image on a background thread and render it bit by bit. In any case, images for use with MIDP application are unlikely to be slow large as to benefit from interlaced rendering.
Can a MIDP application handle pointer events?
Yes; you can override the pointerMoved() and pointerDragged() methods in Canvas.
How can I reduce flicker on my animated Canvas flicker?
As a general proposition, an application that does any kind of animation or rapid display update should minimise the amount of screen that is drawn on each update. However, in a MIDP application the screen is likely to be quite small, and the overhead involved in calculating which area to redraw can easily exceed the time take to redraw the whole display. If you do redraw the whole display, however, you'll see a marked flicker as you erase the background and redraw all the content.
      The solution to this problem is to draw the display off the main screen, then transfer the complete display to the screen as a block transfer operation. This is called `double buffering'.
      Some MIDP devices do their own double buffering. Typically, the Graphics object passed to the Canvas's paint() method is not the context of the real display, but an off-screen buffer. When the paint method returns, the device transfers the buffer to the screen. However, all MIDP applications should do their own double buffering, unless they are sure it is being done by the hardware. It isn't difficult to implement, and it isn't difficult to find out if the hardware is doing it. See the outline code below.
    

import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

class MainCanvas extends Canvas
    {
    protected Image offscreen;
    //... other instance variables

    /**
     * Construct a new canvas, and check for double-buffering
     */
    MainCanvas()
      {
      // ... initialise
      if (!isDoubleBuffered ())
        offscreen = Image.createImage (getWidth (), getHeight ());
      }
 
    /*
     * Draw the display
     */
    protected void paint(Graphics g)
     {
     // If we are double-buffered, work on the main Graphics
     //  object. If we aren't, work on an offscreen buffer
     Graphics g2 = offscreen == null ? g : offscreen.getGraphics();

     // Erase background
     g2.setColor(0x00000000);
     g2.fillRect(0, 0, getWidth(), getHeight());

     // Other drawing ops here...

     // If we aren't double-buffered, transfer the offscreen image
     // to the display
     if (offscreen != null)
       g.drawImage (offscreen, 0, 0, Graphics.TOP | Graphics.RIGHT);
     }
}

What other MIDP/CLDC issues/problems should I be aware of?
In no particular order:

    * You can read an image from a file, and you can create an image from a byte array. But you can't read an image from a file and then get a byte array from it. Consequently, you can't easily process the data in an image supplied as a file (so you can't, say, adjust image contrast programmatically). You may be able to use Class.getResourceAsStream() to read the image into memory, but you'd still have to parse out the pixel data yourself.
    * You can't get raw keyboard events at all in a form-based user interface, or from any item in it.
    * The extremely useful methods in java.util.Date that split a time and date into hours, minutes, etc., are officially deprecated in JDK1.2 and later, but every developer I know still uses them. In the J2ME documentation, these methods are shown as absent completely, and may well not be supported at all in J2ME JVMs. This is probably true of all deprecated methods, but the omission of the Date.get methods is particular irritating.
    * Although Java developers are frequently exhorted to use the ArrayList class instead of the (broadly compatible) Vector class, MIDP/CLDC provides no ArrayList class, only Vector.
    * There is no support for `button' components on forms. In other words, you can't invoke an action by selecting a component with the navigation keys and `pressing' it. MIDP2.0 does support `active' string items, however, which offer similar functionality.
    * An application cannot get a graphics context for redrawing a Canvas except in the paint method. In particular, you can't redraw the canvas in a background thread: the thread must call repaint() on the Canvas, and let the Canvas redraw itself. This limitation makes it easier to implement double-buffering of the display in hardware.

What have you got against the MIDP user interface?
The astute reader will notice, I'm sure, that the things I have been complaining above result in part from exactly the techniques I recommend myself in the questions above on minimising memory requirements. There is nothing wrong with the user interface when seen from the perspective of an 8kB bytecode limit. The designers compromised logical consistency and strict OO principles in order to provide something that would work in such a restricted environment. But one has to ask whether it is still necessary to be as fastidious about memory conservation now that technology has moved on. The Nokia 6600 has 6Mb of internal memory, expandable by plug-in cards. It supports MIDP2.0. Yet MIDP2.0 still has the same illogicalities that MIDP1.0 had; it has to, to retain backward compatibility. In some ways, it would have been better if MIDP2.0 had provided an entirely new user interface model.
Why is there no support for custom classloaders in CLDC/MIDP?
Because, in theory, to provide custom classloader support would be to allow a MIDlet to interact with classes in a different MIDlet suite. This would be a security hazard.
Can a CLDC/MIDP application use native method calls?
The CLDC Specification mandates that CLDC code be forbidden to make native method calls. This is for security reasons. In any case, if you know how to make native method calls on your MIDP hardware device, you probably have the wherewithal to write the application using a programming language that produces native code, such as C or C++. One of the main advantages of using Java - cross-device portability - is lost as soon as you start using native methods, so if you need to call native code, you may as well work in C++.
What do the line numbers mean in an exception stack trace from the WTK?
In an ordinary Java application, when an exception is thrown the JVM will attempt to attach source code line numbers to the elements of the stack trace, provided that debugging information was compiled in. WTK does not do this; a stack trace will look like this:

java.lang.ArithmeticException
at HelloWorld.startApp(+25)
at javax.microedition.midlet.MIDletProxy.startApp(+7)
at com.sun.midp.midlet.Scheduler.schedule(+266)
at com.sun.midp.main.Main.runLocalClass(+28)
at com.sun.midp.main.Main.main(+116)

The numbers `+25', `+7', etc., correspond to the position in the bytecode where the exception was thrown, relative to the start of the method. The only procedure I know to translate this into an exact line number in the source is to compare the source listing with the output of javap -c on the class. This command gives the dissassembled bytecode, which you can relate back to the source listing.
Why would I want to obfuscate J2ME classes?
Bytecode obfuscators replace the meaningful names of variables, classes, and methods in your code with gibberish. The obfuscated classes continue to work the same, but are much harder to reverse engineer. Obfuscation is often used by distributors of Java applications to protect their trade secrets, such as how particular algorithms work. In a J2ME application, however, obfuscation has the additional benefit of tending to produce slightly smaller classes. This reduces the memory footprint of the application, and the time taken to upload it to the device.
How does an application schedule regular timer events?
Best shown by example:

Timer timer = new Timer();
timer.scheduleAtFixedRate (new TheTimerTask(this),
        0, millisecondInterval);
//...

public class TheTimerTask extends TimerTask
  {
  public void run() { /* Do whatever */ }
  }

Timer events are still delivered when the application is paused.
Can a CLDC/MIDP application parse XML?
In principle, certainly. The difficulty might be getting an XML parser into the limited memory of the device. There are a number of J2ME-friendly parsers around, including nanoXML and kXML. If you are using XML for communications, you may prefer to use something like WBXML instead, as it is structurally the same as XML but much more compact. Obviously you will only be able to do this if you have control over both ends of the communications link. WBXML is a binary representation of XML, in which each element name is replaced by a numeric token. Some parsers, such as kXML, have built-in support for WBXML format.
What do I do if I need floating-point maths support?
The first question you should ask is whether you really, really need decimal maths (rather than integers), or whether you'd be better off scaling your quantities to integers instead. For example, you could represent the sum of money `twelve pounds and five pence' as the decimal number 12.05 (pounds). But you could equally well represent it as 1205 (pence). Provided all your amounts are in pence, you'll still get the right answer (in pence) when you add, subtract, multiply, or divide them (but watch out for rounding errors).
      Things are a bit more complicated when you need to scale different quantities by different amounts. For example, if you multiply velocity in metres per second, by time in seconds, you get distance in metres. Suppose you have velocities of millimetres per second, and times of milliseconds? If you scale both these quantities by a factor of 1000, how much will you have to correct the final answer by? Unless you are a physicist, you probably aren't very familiar with working this things out.
      If you really do need decimal arithmetic, your next question should be whether you need floating point maths, or whether fixed point would do. Fixed point is much easier to implement, and it perfectly satisfactory for financial and most engineering applications. It's no different in principle to the process of scaling described above, but with the scaling factors handled automatically by the implementation. There are a number of J2ME-friendly fixed-point maths libraries around. It would also be possible to port one of the standard floating-point libraries to CLDC, but the memory footprint is likely to be excessive.
Should I use vendor-specific extensions to the CLDC/MIDP APIs?
Most vendor-specific extensions are exactly that; there is very little uniformity across the industry. Using these extensions reduces the portability of your applications. On the other hand, CLDC/MIDP provides a high-level abstraction of device functionality. You won't be able to use MIDP, for example, to change the display contrast or place voice calls. If you need to do this kind of thing, you are stuck with using vendor-specific extensions for the time being.
File and data management
Can a CLDC/MIDP application read or write files?
Not necessarily. Most CLDC hardware would not recognise a file anyway. To support files you need some sort of bulk non-volatile storage. However, the lack of files does not indicate a complete lack of non-volatile storage: the application can use the record store API (see below).
What is a record store?
A record store is the only form of persistent storage that a CLDC device is required to support. A record store is a set of unformatted data elements, each identified by a unique integer. There are API calls to create and delete record stores, and to insert, remove, and search the elements. The elements themselves are exposed to the application as byte arrays, and if they are to have any internal structure, then the application must impose it. In this sense, records in a record store a like files, except that (1) the files are numbered, not named, and (2) each record element must be read or written in one operation.
      Because CLDC applications can be multi-threaded, the use of record stores is a rare example of an occasion where concurrency management can be troublesome to a CLDC developer. The record store implementation will synchronise access to the external non-volatile memory to the extent that each read or write is atomic. However, there are no transactional semantics or locking in record stores. If one thread of execution reads the same record twice, and another thread modifies the record between the other threads two reads, then the two reads will get different values. Vendors of relational databases go to some lengths to protect against this sort of problem, but such measures would be difficult to implement in a CLDC device. It is therefore the developer's responsibility to ensure that different threads do not attempt to read and write the same records at the same time if this would upset the application logic.
What can a record store?
A individual record contains a unique integer ID, and a byte array of arbitrary length. So you can store anything in a record that you can represent as a byte array. What if you want to store the state of a Java object? Your first thought might be to serialise the whole object into a ByteArrayOutputStream, then copy the bytes to the record. This is less easy than it should be, because there is no built-in support for serialisation in CLDC. So you'll need to equip your persistent classes with methods to save their state as a byte array, and regenerate their state from a byte array. Not rocket science, but more work for the developer.
Can record stores be shared between MIDlet applications?
In MIDP1.0 (which is the most widely-supported version at present) MIDlets can read and write record stores created by other MIDlets in the same MIDlset suite (that is, deployed in the same JAR file). MIDP2.0 adds support for shared record stores: when a record store is created, the creating application can specify whether its record store is to be accessible to other MIDlet suites or not.
Can record stores be synchronised between devices?
There is no provision for this in the MIDP specification, so if such a feature is provided, it is vendor-specific.
Can record stores be synchronised between mobile devices and workstations or servers?
There is no provision for this in the MIDP specification, but it could be implemented (with some difficulty) by the developer. As the only communication mechanism supported by MIDP1.0 is HTTP (see below), your best bet would be to write a MIDlet that sends and retrieves the records to and from a web server using HTTP requests. The web server would provide a web application (based on servlets, perhaps) that receives the record store elements from the device, and synchronises them with its own version of the record store. If the record store element on the server is more up to date than that on the device (because it has been modified by a different application) then the server could send back to the device the modifications it needs to make to its own record store.
      The process of synchronisation is, of course, much more straightforward if it is one-way. If the purpose of the synchronisation is just to provide a backup feature for the record store, then the synchronisation logic is quite simple. The device will either send a whole record store (backup) or retrieve a whole record store (retrieve). Things are much more complex if the record store could be updated at both ends of the link. Then the process of synchronisation is not just a case of copying a batch of records from one place to another, but requires inspecting the relative timestamps of each record in the record store. It is wrong (and potentially destructive) to assume that just because one record store (say `Store1') was updated more recently than the other (`Store2'), then synchronisation consists only of transferring updates from Store1 to Store2. Suppose that within Store1, Record1 was modified at 1pm and Record2 at 5pm. In Store2, Record1 was modified at 3pm. Clearly Store1 has the later modification time: Record2 at 5pm. But, in fact, Store2 is more up to date in respect of Record1: 3pm rather than 1pm. So the flow of updates is in both directions. It should also be clear that the modification timestamp of the data store itself is of absolutely no value in synchronisation - you must timestamp each record. Unfortunately (sigh) the CLDC record store implementation provides no support for timestamping records. I do not know if this is an oversight (it is a reasonable one - designing database synchronisation logic is a specialist job), or if the types of device that CLDC supports don't have the required functionality in their firmware. I'm reasonably sure that the record store implementation in PalmOS - for which much of the original KVM development was targeted - does not timestamp records.
      What this means for the developer is that you have to encode a timestamp into each record in the record store, along with the real data. This in turn means that you can't implement a general record-store synchronisation application on the web server, because the application needs to know how to unpack each record and extract the timestamp. Thus the MIDlet logic and the server-side synchronisation logic have to be maintained together.
Can a record store be encrypted?
You might want to encrypt a record store to protect access to it other than through the application that manages it. It would defeat the purpose of securing an application against unauthorised use, if the hacker could simply read the record store at the operating system level (on PalmOS, the record stores seen by MIDP can also be downloaded to a PC).
      There is no built-in support for data encryption in MIDP/CLDC. However, it is not difficult to implement a Java version of an algorithm of, say, IDEA or Blowfish. You'll only need a dozen lines of Java code. Alternatively, find an open-source project that has already done it. See, for example, Bouncy Castle.
Networking and communications
What networking operations can a MIDP application perform?
MIDP defines a networking API called the Generic Connection Framework (GCF). This is a highly abstract representation of the fundamental things that an application can to do over a network, that is, send data and receive data. To use the GCF, the application supplies a URL to the (static) Connector class, and gets back an object that implements the InputConnection and OutputConnection interfaces. Methods on these interfaces allow the creation of input and output streams to the remote system. These streams can then be used to send and receive data. Apart from the URL itself, no part of this process depends on the network protocol.
      In practice, a MIDP device will probably support only HTTP, and unless the URL begins http:// the connection will usually fail. Moreover, an HTTP transaction consists of more than simply establishing a data stream in each direction. The device will probably need to set specific HTTP request header items, and parse the HTTP response headers. As a result, MIDP provides a specific implementation of an HTTP connection called, unsurprisingly, HttpConnection. The application can cast the result of the Connector.open() to an HttpConnection, and then do HTTP-specific things on the connection.
      MIDP2.0 extends the GCF by providing API classes for other protocol types, such as raw TCP/IP, UDP, and HTTPS. However, the fact that these classes exist should not fool the developer into assuming that these protocols will be supported. For example, WAP phones do not talk TCP/IP; their HTTP support is based on lower-level WAP protocols, not TCP. So you may still find that you can't open a low-level TCP socket. Even if the device supports other protocols, and the service provider can carry them, you may still find that you have to contend with firewalls. It would seem sensible to stick to HTTP for the time being.
      In principle this isn't a restriction; you can tunnel other protocols on top of HTTP easily enough. In practice it means extra work for the developer.
Can I create a server (listening) socket in a MIDP application?
There is no real equivalent of java.net.ServerSocket, but you can create a socket in such a way that it blocks until an inbound connection is detected. Here is a snippet of code that demonstrates the technique in MIDP1.0.

StreamConnectionNotifier serverSocket =
  (StreamConnectionNotifier) Connector.open("serversocket://:12345");
StreamConnection conn = serverSocket.acceptAndOpen();

In MIDP2.0, a more elegant way of doing this is:

ServerSocketConnection scn =
  (ServerSocketConnection) Connector.open("socket://:12345");

SocketConnection sc = (SocketConnection) scn.acceptAndOpen();

The MIDP2.0 version gives the application a real, TCP/IP socket whose own properties can be manipulated independently of the server socket. The MIDP1.0 version only provides the application with a preconfigured stream. In practice, neither version will work unless the device and the service provider are prepared to support TCP/IP (and they aren't required to).
Why does my application run out of network connections after a while?
This behaviour is frequently reported, and usually arises because the developer hasn't understood the relationship between the Connection objects and the data streams derived from it. In an ordinary Java sockets application, if you derive an InputStream and OutputStream from a Socket, the physical socket resource is actually held in the socket object. In MIDP, the physical resources modelling the connection live in the streams, not the socket. If you don't explicitly close the streams, the underlying socket resource remains connected. Eventually, you run out of sockets.
      As a result, it is considered good practice in MIDP programming to close the connection as soon as the streams have been established. For example:

connection = (HttpConnection) Connector.open
  ("http://...", Connector.READ);
InputStream is = connection.openInputStream();
connection.close();

// Re
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics