📄 aug01_ericg.txt
字号:
J 2 M E T E C H T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE
WELCOME to the Java Developer Connection(sm) (JDC)
Java(tm) 2 Platform, Micro Edition (J2ME(tm))
Tech Tips, for August 20, 2001. This issue covers:
* Building Splash Screens for MIDlets
* Client-Server Communication over HTTP using MIDP and
Servlets
The J2ME Tech Tips are written by Eric Giguere
(http://www.ericgiguere.com), an engineer at iAnywhere
Solutions, inc. Eric is the author of the book "Java 2 Micro
Edition: Professional Developer's Guide" and co-author of the
upcoming "Mobile Information Device Profile for Java 2 Micro
Edition," both books in John Wiley & Sons' Professional
Developer's Guide series.
You can view this issue of the J2ME Tech Tips on the Web at
http://java.sun.com/jdc/J2METechTips/2001/tt0820.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BUILDING SPLASH SCREENS FOR MIDLETS
A splash screen is an informational screen that presents the name
and version of an application, along with any necessary legal
information such as the copyright and trademark notices. It's
something you might want your MIDlets to display on startup.
Adding a splash screen to a MIDlet is easy to do, but there are
a few things to watch out for.
The simplest way to build a splash screen is to use an Alert.
Alerts, which were discussed in detail in the April 16, 2001
J2ME Tech Tip titled "An Introduction to the High-Level User
Interface API: Alerts and Tickers" (see
http://java.sun.com/developer/J2METechTips/20001/tt0416.html#tip2),
are part of the Mobile Information Device Profile (MIDP)
high-level user interface API. An alert displays a message and an
optional image for a set period of time or until the user
explicitly dismisses the alert. So building a splash screen is
simply a matter of building and displaying an alert, as done by
the following method:
public void showSplashScreen(
Display d, Displayable next ){
Image logo = null;
try {
logo = Image.createImage( "/images/logo.png" );
}
catch( IOException e ){
}
Alert a = new Alert( "Time Tracker",
"Copyright 2001 by Nobody, Inc.",
logo, null );
a.setTimeout( Alert.FOREVER );
display.setCurrent( a, next );
}
An alert might not be flexible enough for your needs, however.
To dismiss the splash screen with any key or to display a simple
animation, you'll need to use a Canvas for your splash screen.
For example, you could define a simple splash screen like this:
import java.util.*;
import javax.microedition.lcdui.*;
public class SplashScreen extends Canvas {
private Display display;
private Displayable next;
private Timer timer = new Timer();
public SplashScreen(
Display display, Displayable next ){
this.display = display;
this.next = next;
display.setCurrent( this );
}
protected void keyPressed( int keyCode ){
dismiss();
}
protected void paint( Graphics g ){
// do your drawing here
}
protected void pointerPressed( int x, int y ){
dismiss();
}
protected void showNotify(){
timer.schedule( new CountDown(), 5000 );
}
private void dismiss(){
timer.cancel();
display.setCurrent( next );
}
private class CountDown extends TimerTask {
public void run(){
dismiss();
}
}
}
To display this splash screen, simply create an instance of it.
When you create the instance, you pass to it the MIDlet's
Display object; you also pass to it the screen to activate after
the splash screen is dismissed:
public void showSplashScreen(
Display display, Displayable next ){
new SplashScreen( display, next );
}
The splash screen is dismissed when the user presses any key or
presses on the screen (if the device supports a pointing device).
If neither event occurs, the screen is automatically dismissed
five seconds after it is first displayed.
When should you display a splash screen? Your first thought might
be to make its activation the last line of the MIDlet's startApp
method. Remember, though, that the startApp method can be called
more than once for the same MIDlet instance. The constructor is
not a good place to activate a splash screen because the MIDP
specification does not guarantee that the MIDlet's Display object
(required to display any kind of screen or alert) is initialized
yet. So the startApp method is indeed the place to call a splash
screen, but only once. The startApp method is also where you can
safely obtain the MIDlet's Display object, so combine the two
tasks as in the following MIDlet:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class MyMIDlet extends MIDlet
implements CommandListener {
private Display display;
private Command exitCommand = new Command(
"Exit", Command.EXIT, 1 );
public MyMIDlet(){
}
protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}
protected void pauseApp(){
}
protected void startApp()
throws MIDletStateChangeException {
if( display == null ){ // first time called...
initMIDlet();
}
}
private void initMIDlet(){
display = Display.getDisplay( this );
new SplashScreen( display, new TrivialForm() );
}
public void exitMIDlet(){
notifyDestroyed();
}
public void commandAction(
Command c, Displayable d ){
exitMIDlet();
}
// A trivial UI screen
class TrivialForm extends Form {
TrivialForm(){
super( "MyMIDlet" );
addCommand( exitCommand );
setCommandListener( MyMIDlet.this );
}
}
}
It isn't usually necessary to display a splash screen each time
a MIDlet is invoked. Ideally, you should display a splash screen
the first time the user runs the MIDlet, but not again. To do
this, you need to use the Record Management System (RMS) classes
to store an indication of whether the application has been run.
(The basics of the RMS were covered in the February 20, 2001 J2ME
Tech Tip "Record Management System Basics," see
http://java.sun.com/jdc/J2METechTips/2001/tt0220.html#tip2.)
This approach could be as simple as testing for the existence of
a particular record store (which could even be shared among all
the MIDlets in the same MIDlet suite). You display the splash
screen only if the record store doesn't exist. After you display
the splash screen, you create an empty record store to indicate
that the splash screen has been shown.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CLIENT-SERVER COMMUNICATION OVER HTTP USING MIDP AND SERVLETS
Many, if not most, Mobile Information Device Profile (MIDP)
applications have a server-side piece to them. In other words,
a client-server paradigm is used to offload complicated work from
the limited capabilities of the MIDP device to the more capable
server environment. Because the MIDP 1.0 specification only
mandates support for HTTP, all client-server communication must
be done through an HTTP gateway, of which a web server is one
example (however vendors are free to add support for different
communication protocols). This Tech Tip looks at how MIDlets can
communicate with servlets or JavaServer Pages(tm) (JSP(tm))
running on a web server. The same techniques can also be used to
communicate with server applications written in other languages
such as Perl, but using the Java programming language on both
ends of the communication makes coding simpler.
The first step is to understand how MIDlets can use HTTP to talk
to a web server using the HttpConnection class. This was covered
in the December 18, 2000 J2ME Tech Tip, "Making HTTP Connections
with MIDP"
(http://java.sun.com/jdc/J2METechTips/2000/tt1218.html#tip2).
If you read the Tech Tip, pay close attention to the
HttpConnectionHelper class developed in the tip.
The second step is to configure your web server to support
servlets, JSPs, or both. This is beyond the scope of this Tech
Tip, but there are several web servers or web server extensions,
both free and commercial, that you can use. For example, you
can use the Tomcat servlet engine from the Apache Software
Foundation. Tomcat is a free, open-source web server that is
downloadable from the Jakarta Project binary download site
(http://jakarta.apache.org/site/binindex.html). A good place to
start looking for web servers is the Java Servlet Technology page
(http://java.sun.com/products/servlet/) and the JavaServer
Pages(tm) Technology page (http://java.sun.com/products/jsp/).
Note that if you run your MIDlets in an emulator, a local web
server with servlet or JSP support is all you need. If you want
to run MIDlets on real devices, however, the web server must be
accessible from the Internet, which might require configuration
help from your IT department.
After you complete these basic steps, you're ready to try your
first client-server communication. Use the HTTP POST method in
place of the GET method to transfer arbitrary data from the
client to the server. With GET, the only way to pass data is
using encoded text as part of the request URL. POST is more
flexible because it allows you to specify a specific format for
the data (including binary) and it doesn't have the length
restrictions of the GET method.
To make a POST request using the MIDP's HttpConnection class,
you first obtain the HttpConnection object and then call its
setRequestMethod method:
import java.io.*;
import javax.microedition.io.*;
try {
String url = ....; // a URL
HttpConnection conn = (HttpConnection)
Connector.open( url );
conn.setRequestMethod( HttpConnection.POST );
..... // other code here
}
catch( IOException ioe ){
// handle I/O errors
}
Remember to call setRequestMethod before attempting to obtain
any input or output streams, otherwise the call will have no
effect.
After setting the request method, you should also set any
appropriate HTTP headers. The MIDP Specification requires that
you set the User-Agent and Content-Language headers:
conn.setRequestProperty( "User-Agent",
"Profile/MIDP-1.0 Configuration/CLDC-1.0" );
conn.setRequestProperty( "Content-Language", "en-US" );
You should also consider setting the Accept and Connection
headers:
conn.setRequestProperty( "Accept",
"application/octet-stream" );
conn.setRequestProperty( "Connection",
"close" ); // optional
The Accept header tells the web server what kind of data the
client will accept. Use the MIME type that best describes the
data you expect to receive.
The Connection header controls the "keep alive" feature of an
HTTP connection; this feature lets the client and the server
reuse the same connection for multiple requests. If you plan
to make many requests to the same server over a short period of
time, you might get better performance by not setting the
Connection header. If you don't set the Connection header, the
state defaults to keep alive. Otherwise, you should set the
Connection header value to "close" (as above) to ensure that the
connection is terminated after the server sends its response.
The Connection header is particularly important because of its
close association with the Content-Length header. The
Content-Length header lists the size (in bytes) of the request or
response body. A valid Content-Length header is necessary for
keep alive to work -- it's a requirement of the HTTP 1.1
protocol. If the Content-Length header is missing from the
request, the server won't know how many bytes of data to read,
and the client will have a similar problem if it's missing from
the response. An incorrect value can also cause problems. This is
why during development it's often a good idea to turn off keep
alive; this avoids having either end of the conversation block,
waiting for non-existent data to be received. You can turn keep
alive back on once you're sure the data is being sent and
received correctly.
Now you're ready to send the data along with the POST request.
Assuming that the data is in binary format, this is what you do:
byte[] data = ....; // the data to send
conn.setRequestProperty(
"Content-Length", Integer.toString( data.length ) );
OutputStream os = conn.openOutputStream();
os.write( data );
os.close();
What data format should you use for your communication with the
server? It's really up to you. You could send an XML document,
but XML is very verbose. If the server is written in the Java
programming language, why not use the encoding and decoding
capabilities of DataOutputStream and DataInputStream? In other
words:
// Example of sending some structured data to the server...
DataOutputStream os = new DataOutputStream(
conn.openOutputStream() );
os.writeBoolean( true );
os.writeUTF( "This is a string..." );
os.writeInt( 5 );
os.close();
After you close the output stream, an HTTP POST request is formed
and sent to the web server. The MIDlet should next call
getResponseCode to obtain the servlet's response code to check
for a valid response. Normally, this means checking that the
response code is HttpConnection.HTTP_OK:
int rc = conn.getResponseCode();
if( rc == HttpConnection.HTTP_OK ){
// safe to process the response
} else {
// deal with errors, warnings, redirections, etc.
}
A response code other than HTTP_OK is not necessarily an error.
The web server might redirect you to another URL, for example, in
which case you should resend the data to the new URL (the
HttpConnectionHelper class handles this for you automatically).
Processing the response data is a matter of opening an input
stream to read the response body. If structured data is being
sent back, do the following:
DataInputStream in = new DataInputStream(
conn.openInputStream() );
String msg = in.readUTF();
..... // etc. etc., read other data
in.close();
Alternatively, if the data being sent back is something like an
image, you should check the length of the response to see how
much data is being sent. If the length is not -1, allocate a byte
array of that size and read the data into the byte array:
byte[] data;
int len = conn.getLength();
InputStream in = conn.openInputStream();
if( len != -1 ){
int total = 0;
data = new byte[len];
while( total < len ){
total += in.read( data, total, len - total );
}
} else {
ByteArrayOutputStream tmp =
new ByteArrayOutputStream();
int ch;
while( ( ch = in.read() ) != -1 ){
tmp.write( ch );
}
data = tmp.toByteArray();
}
in.close();
Be sure to respect the response body length. If you try to read
more bytes than were sent, your application will block until the
server decides to timeout the connection. Also, you must always
be prepared to handle the case where the length is not set.
On the server side, things are fairly simple. In a way similar
to how the MIDlet processes the response, the servlet gets the
length of the request body and opens an input stream to process
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -