September 28, 2008 10:00
by
Jorge
In this post I’m going to explore some examples of the approaches to reading HTTP responses in your BlackBerry Java application and how they can impact the application’s performance.
I realize that the performance of your HTTP-handling routines might not impose critical limitations to your application. But if you are considering ways to optimize these routines, I encourage you not only to take a look at these approaches, but also measure their performance characteristics in the context of your application.
The first approach, which I have successfully used in a commercial application, consists of reading the contents of the HTTP response one character at a time. Although slow, this approach is attractive because it is simple and does not require any HTTP-specific behavior from your code:
StreamConnection c = null;
InputStream s = null;
try {
c = (StreamConnection)Connector.open(url);
s = c.openInputStream();
int ch;
StringBuffer response = new StringBuffer();
while ((ch = s.read()) != -1) {
response.append((char)i);
}
} finally {
if (s != null)
s.close();
if (c != null)
c.close();
}
Reading the response data in bulk is a way to gain efficiency. However, this code relies on the underlying connection being able to provide the length of the response:
ContentConnection c = null;
DataInputStream dis = null;
try {
c = (ContentConnection)Connector.open(url);
int len = (int)c.getLength();
dis = c.openDataInputStream();
if (len > 0) {
byte[] data = new byte[len];
dis.readFully(data);
} else {
int ch;
while ((ch = dis.read()) != -1) {
...
}
}
} finally {
if (dis != null)
dis.close();
if (c != null)
c.close();
}
A version of the previous approach that I use in my KnowledgeBase application is this:
HttpConnection c = null;
InputStream in = null;
String response = null;
try {
c = (HttpConnection)Connector.open(url)
in = c.openInputStream();
int len = (int)c.getLength();
if (len > 0) {
int actual = 0;
int bytesread = 0 ;
byte[] data = new byte[len];
while ((bytesread != len) && (actual != -1)) {
actual = in.read(data, bytesread, len - bytesread);
bytesread += actual;
}
response = new String(data);
}
}
While the methods above produce a byte array and thus require a parsing or conversion step – omitted from the sample code – in order to deserialize the data, if your application has intimate knowledge of the response’s contents, you can use specialized methods of the DataInputStream class to read and deserialize the different values in the response in one step:
HttpConnection c = null;
DataInputStream is = null;
int rc;
try {
c = (HttpConnection)Connector.open(url);
rc = c.getResponseCode();
if (rc != HttpConnection.HTTP_OK) {
throw new IOException("HTTP response code: " + rc);
}
is = (DataInputStream)c.openInputStream();
String lastName = i.readUTF();
String firstName = i.readUTF();
int numberOfOrders = i.readInt();
boolean isActive= i.readBoolean();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Not an HTTP URL");
} finally {
if (is != null)
is.close();
if (c != null)
c.close();
}
As you can see, the performance gains have a cost in the amount of complexity of your code and the knowledge of the response contents that your application should have. You will find that this is true for the approaches above as well as other approaches that exist. When deciding on one, consider the tradeoffs and always measure your gains before making a decision.