Tuesday, April 10, 2012

Http Connection Using Thread Handler in Android


This is about how to invoke an Http Url from an android activity. If you have already written such a program in core java, you will not find anything new in the HTTP connectivity part. However, this tutorial also shows how to communicate between thread using the Message object. 


In this tutorial, I plan to download 1 image and 1 text from the internet on the click of respective buttons on the android phone.

NOTE: I have also incidentally used Absolute Layout so that the buttons, the text and the image are all seen on the same screen even after being fetched.

Since fetching data from the internet can be a time-consuming task and a very unpredictable one at that, so, it would best be done in a separate thread rather than the UI/main thread. The basics of this have been discussed in the previous tutorial on Handlers and Threads. 

To go to the application code directly, on the click of a button “Get Image”, I want to get an image whose URL is hard-coded in the program. For that first, I must open an HTTP connection to the server and then request for the image. 

Hence is the code for opening and making an HTTP Connection:

private InputStream openHttpConnection(String urlStr) {
InputStream in = null;
int resCode = -1;
try {


URL url = new URL(urlStr);
URLConnection urlConn = url.openConnection();

if (!(urlConn instanceof HttpURLConnection)) {
throw new IOException ("URL is not an Http URL");
}

HttpURLConnection httpConn = (HttpURLConnection)urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect(); 
resCode = httpConn.getResponseCode(); 


if (resCode == HttpURLConnection.HTTP_OK) {
in = httpConn.getInputStream();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return in;
}
I will not be explaining this code much as most of this is based on the java.net package. This code would be exactly same even if we were to write this in regular java code, not meant for android usage. In brief, I have opened a URLConnection, checked if it is an instance of HttpURLConnection, set the parameters required and made the ‘connection’ finally by calling the connect() method. Then, I check if the response code is OK and I get a handle to the input stream.


So, this is a utility method that I will use for fetching image as well as text.

Now, coming to fetching the image. I want to fetch it when the end user clicks on a button “Get Image”. So the code associated with the button click is here:

getImageButton = (Button)findViewById(R.id.Button01);
getImageButton.setOnClickListener( new OnClickListener() {
Override 
public void onClick(View v) {
downloadImage(http://www.android.com/media/wallpaper/gif/android_logo.gif);
                              }
               });

So the fetching of the image is in the downloadImage() method, which is given the URL of the android logo. Here is the method:

private void downloadImage(String urlStr) {
progressDialog = ProgressDialog.show(this, "", "Fetching Image...");
final String url = urlStr;
new Thread() {
public void run() {
InputStream in = null;
Message msg = Message.obtain();
msg.what = 1;
try {
in = openHttpConnection(url);
bitmap = BitmapFactory.decodeStream(in);
Bundle b = new Bundle();
b.putParcelable("bitmap", bitmap);
msg.setData(b);
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
messageHandler.sendMessage(msg); 

}
}.start();
This method looks a bit complicated. First, let us look at the basics. If I were not fetching this in a separate thread, I would have just the 3 lines of code that is in Bold and highlighted. Open a connection, fetch the bitmap and close the connection. However, since this is a typical task that is unpredictable in its response time, it is best done in a separate thread of its own. So, before I start a new thread, I let the UI thread to show a ProgressDialog as shown in 

progressDialog = ProgressDialog.show(this, "", "Fetching Image...");

Then, I start a new thread. I make the URL string accessible within the new thread by making it a final variable. Since message is an object that I can use for communication between threads, I create aMessage object in the thread. Then, I set the message number to 1, so that I can use it later. 
Message msg = Message.obtain();
msg.what = 1;
Then, I bundle my bitmap already fetched into a Bundle object that can be sent back in the Messageobject.
Bundle b = new Bundle();
b.putParcelable("bitmap", bitmap);
msg.setData(b);

Once I close the input stream as in:
 in.close();
the thread has completed the job. So, I notify the main / UI thread through this method and also pass on the Message object:
messageHandler.sendMessage(msg);
This completes the fetching of the image in a separate thread. Now, how do I retrieve the image from the Message object in the main thread?
As soon as the child thread notifies, the method called back in the main thread is thehandleMessage(msg) method. It is in this method that I retrieve the bitmap and set it to the ImageView in the UI. Here it goes:


private Handler messageHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
ImageView img = (ImageView) findViewById(R.id.imageview01);
img.setImageBitmap((Bitmap)(msg.getData().getParcelable("bitmap")));
break;
case 2:
    ……….
}
progressDialog.dismiss();
}
        };

Within this method, first I check the msg.what variable to see what the type of message I am expecting is. If it is 1, which I had set in downloadImage(..) method, then, I do the required things to get a handle to the ImageView object and then give the bitmap to it.

How do I fetch the data from the msg object? Through getData(). Then I use the key “bitmap” to retrieve the bitmap and cast it to Bitmap before setting it to the ImageView. Finally, I dismiss the progress dialog.

I hope this is clear. This example not only shows HTTP Connection from Android but also the Thread-to-thread communication through handler, message exchange.

In a very similar fashion, I also fetch the text. On click of the Get Text button, here is the code that gets invoked:
getTextButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
downloadText(http://saigeethamn.blogspot.com/feeds/posts/default);
}
         });

The downloadText() code is here:

private void downloadText(String urlStr) {
progressDialog = ProgressDialog.show(this, "", "Fetching Text...");
final String url = urlStr;
new Thread () {
public void run() {
int BUFFER_SIZE = 2000;
InputStream in = null;
Message msg = Message.obtain();
msg.what=2;
try {
in = openHttpConnection(url);
InputStreamReader isr = new InputStreamReader(in);
int charRead;
text = "";
char[] inputBuffer = new char[BUFFER_SIZE];
while ((charRead = isr.read(inputBuffer))>0)
{ 
//---convert the chars to a String---
String readString = 
String.copyValueOf(inputBuffer, 0, charRead); 
text += readString;
inputBuffer = new char[BUFFER_SIZE];
}
Bundle b = new Bundle();
b.putString("text", text);
msg.setData(b);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
messageHandler.sendMessage(msg);
}
}.start();
}

And the way the text is handled in the main thread handleMessage(..) method is here:
case 2:
TextView text = (TextView) findViewById(R.id.textview01);
text.setText(msg.getData().getString("text"));
break;
The complete code can be downloaded here 

No comments:

Post a Comment