Over the last few days I've been writing a MIDlet to collect GPS coordinates and cell identifiers. I'm doing this in an effort to look at what algorithms might be needed in order to implement something similar to Google's My Location service. Here is a Google Earth example of the data I'm collecting. Yesterday, I reached a point where I was collecting all the information I needed, but the program was often plagued by random disconnections of the Bluetooth link to the GPS.
In general, I've been favorably impressed with the capabilities of Java's MID profile, its documentation, and Sun's Wireless Toolkit. Thanks to various JSRs providing access to phone capabilities that were once fenced off, the walled garden approach I complained about two years ago seems to be becoming less of a problem. Applying my (mediocre) Java coding skills to the new platform was trivial; in a couple of hours after installing the WTK I had an application running on a mobile phone. Device interoperability is also impressive. The interface of the application I developed on the emulator and tested on a Nokia 7373 ran without a problem on a SonyEricsson K770i.
Back now to the problem I was experiencing.
A web search convinced me that unbuffered reading of bytes from the Bluetooth
connection was a probable drag on performance.
A look at Sun's implementation of the various I/O classes (thanks Sun
for making the source code easily available) convinced me that
indeed each byte I read probably resulted in a separate inefficient system
call.
I also found a
solution,
based around copying data into a
ByteArrayOutputStream
and retrieving it from there.
I didn't like the idea of writing ad-hoc buffering code within my Bluetooth
processing loop, so, instead, I wrote a minimal subset of Java's
BufferedInputStream
class.
/*
* @author: Diomidis Spinellis
*
* $Id: BufferedInputStream.java,v 1.1 2008/01/04 10:41:04 dds Exp dds $
*
*/
package gr.spinellis.cellmap;
import java.io.InputStream;
import java.io.EOFException;
import java.io.IOException;
/**
* Provide a buffering layer to an input stream
*/
class BufferedInputStream {
/** Underlying input stream */
private InputStream in = null;
/** Buffer used for storing available data. */
byte buff[];
/** Number of available bytes in the buffer. */
int nByte;
/** Position of the next byte to read. */
int readPos;
/** Position of the next byte to fill. */
int fillPos;
/** Size of the buffer. */
int buffSize;
/** Default size of the buffer. */
private final int defaultBuffSize = 1024;
/** Create a buffered input for the specified stream. */
BufferedInputStream(InputStream in) {
this.in = in;
buff = new byte[buffSize = defaultBuffSize];
readPos = nByte = 0;
}
/** Return the next available byte or -1 if EOF is reached. */
public int read() throws IOException {
if (nByte == 0)
try {
fillBuff();
} catch (EOFException e) {
return (-1);
}
nByte--;
if (readPos == buffSize)
readPos = 0;
return buff[readPos++];
}
/** Fill the buffer with at least one byte. */
private void fillBuff() throws IOException, EOFException {
// Bytes we can read without blocking.
int inAvail = in.available();
// Bytes we can fill
int buffAvail = buffSize - fillPos;
int toRead = Math.min(inAvail, buffAvail);
int nRead = in.read(buff, fillPos, toRead);
if (nRead == 0)
throw new EOFException();
fillPos += nRead;
if (fillPos == buffSize)
fillPos = 0;
nByte += nRead;
}
/** Close the input stream. */
public void close() {
try {
if (in != null) {
in.close();
in = null;
}
} catch (Exception ex) {
Logger.getDebugInstance().message("Exception closing BufferedInputStream: " + ex);
}
}
}
The approach worked perfectly.
BufferedInputStream
is included in Java's MID profile.BufferedInputStream
class.Last modified: Friday, January 4, 2008 1:13 pm
Unless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.