How to the write universal MIDlets?
Nowadays most of the mobile phones support mobile Java applications.
Unfortunately the supported features (JSRs) greatly differ from phone
to phone. If you want to write an application, which can run on all
J2ME enabled devices (may be only restricted functionality can be provided)
you have to remove the dependencies on the JSRs. If your application has
a direct reference to a missing JSRs' class when you start the
application it will throw an exception, which you cannot catch.
To overcome this problem you have to remove all the direct references
from your application to the additional JSRs.
Here is an example how you can do this:
There are at least 2 methods to detect if a device supports a special JSR:
- 1. If the JSR has a system property value: e.g. microedition.pim.version
for JSR 75 PIM, checl if the property is null or not like this: if (System.getProperty("microedition.pim.version") != null)
- 2. Use the Class.forName() for a JSR's class e.g.
Class.forName("javax.bluetooth.LocalDevice") for JSR 82
and then catch the ClassNotFoundException and the others if the the API is not present (check the example for more details)
The example first try to use the JSR-179 Location API to get the last known location data,
if the API is not available on the device it will try to use JSR-82 Bluetooth API to get the data
directly from a BT GPS by parsing the NMEA protocol (of course the whole functionality is not implemented!;-))
The MIDlet will print out which method is used and some location data or it will print out there is no location provider
available if neither JSR-179 nor JSR-82 is available on the device.
First here is a utility class:
src/hu/motoros/mobile/Coords.java |
package hu.motoros.mobile;
public class Coords {
double longitude;
double latitude;
float altitude;
public Coords(double longitude, double latitude, float altitude) {
this.longitude = longitude;
this.latitude = latitude;
this.altitude = altitude;
}
public String toString() {
return "longitude: "
+longitude+", latitude: "
+latitude+", altitude: "+altitude;
}
}
To remove the direct reference from your application to the JSR's classes
put all the functionality behind an interface e.g.:
src/hu/motoros/mobile/UniversalLocationProvider.java |
package hu.motoros.mobile;
public interface UniversalLocationProvider {
public Coords getLastLocation();
public String getName();
}
Move all your JSR dependant code to separate classes from your MIDlet e.g.:
src/hu/motoros/mobile/JSR179LocationProvider.java |
package hu.motoros.mobile;
import javax.microedition.location.Location;
import javax.microedition.location.LocationProvider;
import javax.microedition.location.QualifiedCoordinates;
public class JSR179LocationProvider implements UniversalLocationProvider {
public Coords getLastLocation() {
Location loc = LocationProvider.getLastKnownLocation();
if (loc != null) {
QualifiedCoordinates coords = loc.getQualifiedCoordinates();
return new Coords(coords.getLongitude(),
coords.getLatitude(), coords.getAltitude());
} else return new Coords(23.2, 12.1, 67.6f);
}
public String getName() {
return "JSR179LocationProvider";
}
}
And:
src/hu/motoros/mobile/BTLocationProvider.java |
package hu.motoros.mobile;
public class BTLocationProvider implements UniversalLocationProvider {
public Coords getLastLocation() {
return new Coords(0.0, 1.0, 15.0f);
}
public String getName() {
return "BTLocationProvider";
}
}
In your applications class check the available JSRs and use the Class.forName() to instantiate the actual
LocationProvider e.g.:
src/hu/motoros/mobile/UniversalMIDlet.java |
package hu.motoros.mobile;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class UniversalMIDlet extends MIDlet implements CommandListener {
Display display;
Form logger = new Form("Universal MIDlet");
UniversalLocationProvider locProvider;
public UniversalMIDlet() {
display = Display.getDisplay(this);
init();
}
public void startApp() {
display.setCurrent(logger);
if (locProvider != null) {
logger.append("LocationProvider = "+locProvider.getName());
Coords coords = locProvider.getLastLocation();
logger.append(coords.toString());
} else logger.append("No available LocationProvider");
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
notifyDestroyed();
}
public void commandAction(Command c, Displayable d) {
}
public void init() {
if (System.getProperty("microedition.location.version") != null)
try {
locProvider =
(UniversalLocationProvider)
Class.forName("hu.motoros.mobile.JSR179LocationProvider")
.newInstance();
}
catch (ClassNotFoundException e) {e.printStackTrace();}
catch (InstantiationException e) {e.printStackTrace();}
catch (IllegalAccessException e) {e.printStackTrace();}
else {
try {
Class.forName("javax.bluetooth.LocalDevice");
locProvider = (UniversalLocationProvider)
Class.forName("hu.motoros.mobile.BTLocationProvider")
.newInstance();
}
catch (ClassNotFoundException e) {e.printStackTrace();}
catch (InstantiationException e) {e.printStackTrace();}
catch (IllegalAccessException e) {e.printStackTrace();}
}
}
}
With this method your application is not tied to any JSRs and it will run on
most platforms, but it won't be able to provide the full functionality,
but still it will run!
Obviously to compile your code you will need the stubs of the JSRs, but after
that you don't have to port your code to different devices, your application
is one step closer to the write once run anywhere concept!;-)
You can download the MIDlet to your mobile from http://mobile.motoros.hu/examples.wml or from http://mobile.motoros.hu/Example1.jad
The source code and the binaries can be downloaded from here
If you have comments or questions don't hesitate to contact me at
h.gergo@freemail.hu
Have fun!
Gergo
|