|
|
|
Home > Computing > Sofware development
A J2ME FAQ
Last modified: Fri Aug 3 08:24:50 2007
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...
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();
// Read from the input stream...
is.close();
The a regular Java programmer this looks odd, because it would
be assumed that closing the socket closes the streams.
Can a MIDlet determine the IP number of its host?
Probably not in any robust way. Mobile devices don't necessarily
use IP. In MIDP2.0, a device can open a ServerSocketConnection,
and then call its getLocalAddress() method to get its
own address. However, this comes back as a String, which
is not guaranteed to contain an IP number. Even if it does, the
application will have to parse it.
Can a MIDlet determine the telephone number of
the mobile phone hosting it?
Not in any portable way, because the MIDP specification
is not limited to mobile phones. The MIDP2.0 API
provides a method MIDlet.platformRequest()
that a MIDlet can call to ask the runtime system for
platform specific data. It is envisaged that telephone details will
be made available this way. Of course, this is still vendor-specific,
but at least does not require the use of vendor-specific classes.
Can a MIDlet send SMS/MMS messages
Only if the device supports the WMA optional package. At
the time of writing, most don't.
|
|
|
|
Shameless plug
|
 By the author of this site. Buy on-line from Amazon USA | UK
|
|