×
Namespaces

Variants
Actions

Reading an InputStream in Java ME

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Article
Created: grahamhughes (22 Jan 2010)
Last edited: lpvalente (27 Apr 2014)

Contents

Overview

This article explains three methods for reading input streams in Java ME.

Reading an Array of Bytes

There are two popular methods of reading an array of bytes (for example, from an HttpConnection or a JAR resource file).

One is to use a loop, calling:

while ( (b = in.read()) != -1 ) {
// store the byte somewhere
}

This requires a method call for every byte read, potentially tens of thousands of method calls. Method calls in Java have a very large overhead, especially calls to non-private instance methods. Methods that do only a very small amount of work (such as reading a single byte) are extremely inefficient. On some devices, code like this can be excruciatingly slow.

The other (popular) technique is:

byte[] data = new byte[theAmountOfDataIWant];
in.read(data);

This is much faster. And it will work - on some devices. It is based on the belief that the read(byte[]) method will continue reading until either:

  1. the byte[] is filled, or
  2. the end of the stream is reached.


However, there is a third possibility.

  1. the InputStream has read as much as it wants, and decides not to read any more.


This possibility leads to some confusion. Some implementations like to read in fixed-size chunks, and will stop reading once a chunk is read. When using this form of read(), you must check the return value, which tells you how many bytes were actually read.

Reading a Known Number of Bytes

Ideally, you know how many bytes you want to read. This is the recommended practice, as it is the most efficient on memory and the easiest to code. Where possible, prefix the data with a length, so you know how much to read.

Simplest way, use a DataInputStream:

DataInput din;
 
// insert code to acquire a DataInputStream here
 
byte[] data = new byte[theAmountOfDataIWant];
din.readFully(data);

If, for some reason, you don't want to use a DataInputStream, you can easily do without.

private static byte[] readKnownLength(InputStream in, int length) throws IOException {
byte[] data = new byte[length];
int readSoFar = 0;
while (readSoFar < length) {
readSoFar += in.read(data, readSoFar, length - readSoFar);
}
return data;
}

Note that this is only appropriate when you can guarantee the length. For simplicity, I have not included a check for reaching the end of file before length bytes are read.

Reading Until the Stream Ends

Often, you don't know in advance how many bytes to read. This happens when:

  • You are reading from an HttpConnection that has no content-length
  • You are reading a resource file from the JAR in a format you do not control (such as a PNG or MIDI file)


In this case, you must keep reading blocks of data, and appending them to a buffer, until there is no more data to read.

private static final int BUFFER_SIZE = 512;  // multiples of this are sensible
 
private static byte[] readToEndOfStream(InputStream in) throws IOException {
byte[] data = new byte[0];
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
 
while ( (bytesRead = in.read(buffer)) > 0 ) {
// construct large enough array for all the data we now have
byte[] newData = new byte[data.length + bytesRead];
// copy data previously read
System.arraycopy(data, 0, newData, 0, data.length);
// append data newly read
System.arraycopy(buffer, 0, newData, data.length, bytesRead);
// discard the old array in favour of the new one
data = newData;
}
return data;
}

This time, a small, fixed-size buffer is used to read blocks of data. A second array variable refers to a byte array containing all the data read so far. Each time a new block is read, a new array is created, big enough to hold all the previous data and the new data we have just read. The old and new data are copied into the new data array, and the old data array is discarded.

This creates a lot of garbage. However, that is unavoidable. You can use other, similar techniques (that involve appending each block to a Vector and assembling them at the end, or writing each block to a ByteArrayOutputStream), but these will generally consume more memory and create at least as much garbage.

Note that I'm reading until zero is returned from read(), not -1. This is because a bug in some devices causes them to return zero at the end of the stream, rather than the correct -1. Since read() blocks until at least some data is available, a correct implementation will only return zero if the length argument is zero. Using zero rather than -1, therefore, still works, and prevents the code from looping forever on devices with this bug.

This page was last modified on 27 April 2014, at 17:33.
101 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×