BlackBerry: Sending an HTTP request in a separate thread

Sending an HTTP request in a separate thread is a very common operation for almost every client-server web application. The idea is to spin-off a background thread that will send a request and process the response data, but won’t block the main (GUI) thread on the same time.  At the end of its execution the background thread should use some kind of a callback mechanism to pass response results back to the main thread. Since this operation is very common I was expecting that pretty much every modern framework should have a lot of tutorials available online describing how to implement this functionality in a quick and easy way. However this was not the case for the BlackBerry Java API -  after a couple of hours researching I still didn’t find anything that was ‘quick and easy’…

Finally, looking through RIM’s API docs I found a method Application.invokeLater() which can take a runnable object defined in a background thread and start it in the main thread. This method allowed me to pass execution control back to the main thread.

Here is a sample code:

First let’s create a ConnectionThread class that can take a URL (to send a request to) and call a method of some screen once the response is received (for the sake of simplicity we’ll be calling a method of the screen that is currently on top of the screen’s stack).

public class ConnectionThread extends Thread {
	String URL;

	public ConnectionThread(String URL) {
		this.URL = URL; //URL to send a request to
	}

	public void run() {
		//optional: show some popup "Please wait screen"
		final Screen dialogScreen = new WaitPopupScreen(); //wait popup screen extends RIM's PopupScreen class
		UiApplication.getUiApplication().invokeLater(new Runnable() {
			public void run() {
				UiApplication.getUiApplication().pushModalScreen(dialogScreen);
			}
		});

		//send HTTP request and save the response
		HttpConnection connection = null;
		//use API 5.0 Connection factory class to get first available connection
		byte responseData[] = null;
		try { 
			connection = (HttpConnection) new ConnectionFactory().getConnection(URL).getConnection();
			int len = (int) connection.getLength();
			responseData = new byte[len];
			DataInputStream dis;
			dis = new DataInputStream(connection.openInputStream());
			dis.readFully(responseData);
		} catch (Exception e) {
			 // TODO Handle exception 
		} 

		final byte[] responseDataToProcess = responseData;
		//use invokeLater method to pass results back to the main thread
		UiApplication.getUiApplication().invokeLater(new Runnable() {
			public void run() {
				UiApplication.getUiApplication().popScreen(dialogScreen); //hide wait popup screen
				//pass results to the callback mathod of the current screen
				((MyScreen)UiApplication.getUiApplication().getActiveScreen()).callBackMethod(responseDataToProcess);
			}
		});
	}
}
This is the actual screen where we spin the connection thread off:
public class MyScreen extends MainScreen {
	public MyScreen() {
		super();
		// add a button that will initiate an HTTP request
		ButtonField requestButton = new ButtonField("Send HTTP Request");
		requestButton.setChangeListener(new FieldChangeListener() {
			public void fieldChanged(Field field, int context) {
				//start connectionThread on a button click
				new ConnectionThread("http://mnarinsky.com").start();
			}
		});
		add(requestButton);
	}
	
	//this method will be called from the connection thread
	public void callBackMethod(byte[] responseData){
		//process response
	}
}
Finally this is a code for an optional 'Please wait' popup screen – displays a simple “Please wait..” text. If you want you can make it little more fancy by using some animated GIF image.   
public class WaitPopupScreen extends PopupScreen {
	public WaitPopupScreen() {
		super(new VerticalFieldManager());
		LabelField labelField = new LabelField("Please wait...",
				Field.FIELD_HCENTER);
		add(labelField);
	}
}

30 comments:

Anonymous said...

Thank you so much! You're amazing.

Hai-Long Nguyen said...

awesome

Nirvana Tikku said...

Great post, Mike. Thanks!

Anonymous said...

post is quite good.But i have doubt on concepts of what is and when to use runnable and extends threads.
Can u help me in clearing this.

Michael Narinsky said...

Not sure what you mean, but you can follow these links to read more about Thread class and Runnable interface:

http://www.javaworld.com/javaworld/jw-04-1996/jw-04-threads.html

http://download.oracle.com/javase/6/docs/api/java/lang/Thread.html

http://download.oracle.com/javase/6/docs/api/java/lang/Runnable.html

Anonymous said...

is this working ???
having a error in - new WaitPopupScreen();
give a advice for me ,i am a beginner..

Michael Narinsky said...

What is your error?

Marvin said...

Hey, great job.

Just tested. Just an error with missing try-catch but no problem.

Second error is:
Cant connect to google.com or any other website
tested with simulator (torch) and real torch
Errormessage on simulator: NegativeArraySizeException

any idea?

Marvin said...

Me again.

Havnt started MDS. But have it key-signed and started in my real device. Just the waitpopup comes up. Nothing more.

Any idea? :)

Michael Narinsky said...

Marvin, thanks.
I was aware of the missing try-catch block. I had it that way since I wanted the developer to implement error handling himself. I've changed it now though, in order to make this post's solution complete.

The second error you've seen happens because of an issue with sending requests specifically to www.google.com

I've updated the post's code, so you can try again - it should work now.

Michael Narinsky said...

Well, you should not really see anything other than the Wait Popup, since the MyScreen.callBack() method is empty. Make sure that responseData array is getting populated with the response data. If it is, than it's working.

You can also add some logic to check response's HTTP code...

Marvin said...

This is my callBack:
public void callBackMethod(byte[] response){
//process response
if(response != null && response.length > 0){
add(new LabelField(new String(response)));
}
}

In simulator works fine, but not on my real BB-Torch.
The app starts, i hit the button and on the right corner i see the transfer arrows and the waiting popup.
After a while (2mins) the popup get closed and no more happend.
Dont know why.

simulator is perfect, real device not Oo

Marvin said...

Me again ;)

Have found my mistake.

on http://www.blackberry.com/knowledgecenterpublic/livelink.exe/fetch/2000/348583/800451/800563/What_Is_-_Different_ways_to_make_an_HTTP_or_socket_connection.html?nodeid=826935&vernum=0 is discribed how you make a connection.

I have tryed:
connection = (HttpConnection) new ConnectionFactory().getConnection(URL + ";deviceside=true").getConnection();

and it works ;)
Now my real device establishing a real TCP-connection - I think ;)

Michael Narinsky said...

Marvin, the fact you had to add ";device=true" suggests that most likely you have an issue with either your BES or BIS connections on your device.

You also don't have to modify URL by yourself in order to specify the type of connection you want - that's what RIM's ConnectionFactory should do for you. I'm using ConnectionFactory object to get any available connection, but you can override this class to customize it for your needs.

More about ConnectionFactory class:
http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/io/transport/ConnectionFactory.html

http://supportforums.blackberry.com/t5/Java-Development/Sample-Code-Using-the-ConnectionFactory-class-in-a-BrowserField/ta-p/532860

Marvin said...

Actually i have no BES-connection. Server down. Maybe thats the problem.
However, it works :)

Have you a solution to send a file via http (post) ?
Dont know how to get the bytearray of a file :)

Michael Narinsky said...

Marvin, take a look a this thread:
http://stackoverflow.com/questions/5005648/blackberry-send-a-httppost-request/5085858#5085858

Marvin said...

I have seen this site. But dont know, how ich get the bytearray of a file to send it :]

Something with:

FileConnection fc = (FileConnection)Connector.open("file:///" + file, Connector.READ);
And after?.. How can i read the file?
FileInputStream, maybe?

Prasad - Sri Lanka said...

Thanks a lot.
I could get some help for my application from ur article

Thanks again.

Anonymous said...

Hi! I love your example! It's so difficult to find resources on BB app development..

But I get ClassCastException on this line:

((MyScreen)UiApplication.getUiApplication().getActiveScreen()).callBackMethod(responseDataToProcess);

No error if I comment out this line but it will not return the results due to that.

Michael Narinsky said...

But I get ClassCastException on this line:
((MyScreen)UiApplication.getUiApplication().getActiveScreen()).callBackMethod(responseDataToProcess);


Make sure that your MainScreen class is called 'MyScreen'. Also you can set a breakpoint on that line and check in the debugger the type of the UiApplication.getUiApplication().getActiveScreen() object. This might give you a clue why your're getting the ClassCastException.

Iyenemi Tyger said...

Please I am having an issue on the callback method I am trying to do this
Public void callBackMethod(byte[] responseData){
String val = new String(responseData);
If(val.equals(“no-user“)
{

Dialog.alert(“User does not exists “);
}

}
It throws errors on th Dialog display part please any ideas @ all.

Michael Narinsky said...

Iyenemi, what is the error message?

DotNet said...

can we sownload a full project of this post ?

thank you

Anonymous said...

http://pastebin.com/FQ1zCB7z

Mine isn't working, why ?

som3a said...

what do i import ?

som3a said...

what do i import ?

Anonymous said...

Nice post thanks dude

Unknown said...

I am getting null pointer exception after waiting for about 2 minutes
can you help me please?

cara hosting said...

where i should put my paramater and value?

Anonymous said...


some animated GIF image.

This link is not working. Shows only a blank page. Please do the needful.