Thursday, September 13, 2012

Android Main thread says :I Still know what you did last summer


The latest version of android does not allow network activities on its main thread. It is a very good principle, so that the app will appear to load faster and you can do the costly operations in the background. So what would you do if you want to make a network connection when the app loads? One of the recommended solution android provides is the use of Async Task. This class is a wrapper over the java timer task and will do the processing in a seperate thread. This design provides an opportunity for the main thread to continue its job while the async task work in the background to finish the networking activities.

Everything sounds good, but there is a pitfall in the AsyncTask life cycle methods and I have tasted a bit of it. I was developing an android application that required a http network connection. So I have created an async task and used to to do the networking activities there. Surprisingly when I run the app, it gives me an NetworkOnMainThread error. I was so curious how this is happening because I already created an AsyncTask that is supposed to be running on a different thread. Then I did some googling and closer look at the life cycle methods of AsyncTask and found the issue is the way I have utilized doInBackground and onPostExecute methods. Here is it how it happened

In doInBackground method I have opened the URL connection and obtained the inputstream and then send it to the onPostExecute method where I have read the text from the input stream. Here is the stupid code I have written :)

protected InputStream doInBackground(String... urls) {
try {
InputStream is = (InputStream) new URL(urls[0]).getContent();
return is
} catch (Exception e) {
   System.out.println("Execution=" + e);
   return null;
}
    }

protected void onPostExecute(InputStream is) {        
InputStreamEntity inputEntity = new InputStreamEntity(is, is.available());// this is an issue
String text = EntityUtils.toString(inputEntity); //this is not correct
System.out.println(text);
}

Did you notice what is wrong with this code? it looks ok to me but android app says I know you are utilizing main thread. The catch here is you have to complete all your networking activities within the doInBackground method. If you notice above code, onPostExecute is making a read on the url input stream, which is not allowed. So i have to change my program to something like this to make it work.

protected String doInBackground(String... urls) {
try {
InputStreamUtil inputUtil = new InputStreamUtil();
InputStream is = (InputStream) new URL(urls[0]).getContent();
InputStreamEntity inputEntity = new InputStreamEntity(is, is.available());
String text = EntityUtils.toString(inputEntity);
return text
} catch (Exception e) {
   System.out.println("Execution=" + e);
   return null;
}
    }

protected void onPostExecute(String text) {        
System.out.println(text);
    }

Summary:If you are using AsyncTask, complete all your network activities within the doInBackground method

njoy
Sajith

No comments: