Introduction
In the last three articles, I’ve been walking you through the creation of an end-to-end BlackBerry application that will serve as a mobile front-end to my Knowledge Base sample web application.
In the third part of this series, I finished the areas of the application that store and retrieve data from the device’s memory. Together with storing data on the device, the ability to use the network is vital for most applications. Today, I will be showing you how the application will communicate with its server-side service.
But first, let’s do a little refactoring.
The Options Class is Now the DataStore Class
Yes, after working with the Options
class for a while, I decided that, since it not just deals with application settings, a better name for it would be DataStore
. “DataStore” better explains the intent of the class.
Making HTTP Requests
There is a plethora of information regarding HTTP connections. I will leave most of it out of this article, and focus only on what’s strictly needed for our application to be able to send HTTP requests and consume HTTP responses.
Two things I should mention are the javax.microedition.io.HttpConnection
interface, which defines the necessary methods and constants for an HTTP connection, and javax.microedition.io.Connector
, the Factory class for creating connections.
For my networking tasks, I have reused and adapted some code that I obtained from the existing BlackBerry sample code online. The bulk of the HTTP handling code will conform to contracts defined in two separate interfaces, HTTPTransport
and HTTPTransportListener
.
HTTPTransport Interface and the KbHTTPTransport Class
HTTPTransport
is an interface that defines the methods for creating HTTP requests.
public interface HTTPTransport {
public void send(final String requestMethod,
final String URL, final String requestData);
public void send(final String requestMethod,
final String URL, final String requestData,
final String[] headerKeys, final String[] headerValues);
public void setHTTPTransportListener(HTTPTransportListener t);
public void removeHTTPTransportListener();
}
KbHTTPTransport
is a helper class which can make HTTP requests by implementing the methods defined in HTTPTransport
. KbHTTPTransport
will notify an attached HTTPTransportListener
with the response to the request. HTTPTransportListener
is an interface that defines the methods a class must have in order to process the response from an HTTP request.
Implementing HTTPTransportListener Inside the Articles Screen
So far, the only place in the application where there’s a need to send HTTP requests is the Articles Screen. Implementing HTTPTransportListener
enables our Articles Screen to process HTTP responses:
public void processResponse(final int HTTPResponseCode, final String data) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run()
{
switch (HTTPResponseCode) {
case (KbHTTPTransport.NOT_IN_COVERAGE):
Dialog.alert("Not in coverage.");
break;
case HttpConnection.HTTP_OK:
parseResponseData(data);
break;
default:
Dialog.alert("There was an error with the request.");
break;
}
}
});
}
Choosing a Transmission Format
From the multiple formats for transmitting serialized data over the net, namely XML, binary, etc., I decided to send and receive data in the form of strings, where the contents of a response are a custom-formatted strings that represent a list of articles.
A formatted list of articles would look like this:
id^~^title^~^dateCreated^~^author^~^tag1,tag2,...^~^contents~^~
where the sequence “^~^” separates the fields of an article, and the sequence “~^~” separates one article from the next.
Notice how this is a pretty simple format that is both easy to use and light in terms of the number of characters transmitted (think about how verbose XML is).
The parseResponseData()
routine is where we de-serialize the response string and create a list of Article
instances.
private void parseResponseData(String data) {
Vector articlesVector = new Vector();
int index = 0;
int length = data.length();
while (index != -1 && index < length - 1){
if (index == 0) index = -1;
Article article = new Article();
article.id = data.substring(++index ,
index = data.indexOf(FIELD_SEPARATOR, index));
index += 3;
article.title = data.substring(index ,
index = data.indexOf(FIELD_SEPARATOR, index));
index += 3;
article.dateCreated = data.substring(index,
index = data.indexOf(FIELD_SEPARATOR, index));
index += 3;
article.author = data.substring(index,
index = data.indexOf(FIELD_SEPARATOR, index));
index += 3;
String tags = data.substring(index,
index = data.indexOf(FIELD_SEPARATOR, index));
article.tags = StringUtilities.stringToWords(tags);
index += 3;
article.contents = data.substring(index,
index = data.indexOf(RECORD_SEPARATOR, index));
index += 2;
articlesVector.addElement(article);
}
articles = new Article[articlesVector.size()];
articlesVector.copyInto(articles);
articlesList.set(articles);
}
Testing the Networking Code
At this point in time, I need to be able to test my networking code without the presence of the server side code (it doesn’t exist yet). In order to achieve this, I have built an implementation of HTTPTransport
called MockHTTPTransport
, which creates different kinds of responses that simulate the scenarios that could occur when working with HTTP requests – timeouts, failed authentication, etc.
class MockHTTPTransport implements HTTPTransport {
private HTTPTransportListener listener = null;
MockHTTPTransport() { }
public void setHTTPTransportListener(HTTPTransportListener t){
listener = t;
}
public void removeHTTPTransportListener(){
listener = null;
}
public void send(final String requestMethod, final String URL,
final String requestData){
send(requestMethod, URL, requestData, null, null);;
}
public void send(final String requestMethod, final String URL,
final String requestData, final String[] headerKeys,
final String[] headerValues) {
createArticlesListResponse();
}
private void createArticlesListResponse() {
Article[] articles = new Article[15];
Article article;
for (int i = 0; i < 15; i++) {
article = new Article();
article.id = String.valueOf(i);
article.title = "Dummy article " + Integer.toString(i);
article.author = "doej";
article.contents = "This is a test article";
article.tags = new String[] {"tag 1", "tag 2", "tag 3"};
article.dateCreated = "01/01/2008";
articles[i] = article;
}
String FIELD_SEPARATOR = "^~^";
String RECORD_SEPARATOR = "~^~";
StringBuffer response = new StringBuffer();
for (int i = 0; i < articles.length; i++) {
article = articles[i];
StringBuffer tags = new StringBuffer();
for (int j = 0; j < article.tags.length; j++) {
tags.append(article.tags[j] + ",");
}
tags.deleteCharAt(tags.length() - 1);
response.append(article.id + FIELD_SEPARATOR +
article.title + FIELD_SEPARATOR +
article.dateCreated + FIELD_SEPARATOR + article.author +
FIELD_SEPARATOR + tags + FIELD_SEPARATOR + article.contents +
RECORD_SEPARATOR);
}
int httpResponseCode = HttpConnection.HTTP_OK;
try {
Thread.sleep(1500);
} catch (InterruptedException ex) {}
listener.processResponse(httpResponseCode,response.toString());
}
What’s Next
The next article of this series will be dedicated to the server side of the application. On the server side, I will put together the code to handle incoming HTTP requests, retrieve information from the database, and send responses back to the application on the BlackBerry device.
Previous Articles