Xero Xero - 1 month ago 4
Android Question

Android issues with AsyncTask and InputStream

I've been trying to figure this out on my own for quite a while.. by trial/error as well as research, but I just can't seem to get it figured out. I'm pretty bad with Async and network connections and stuch, so it might be something simple that I'm over looking. Anyway... I'll paste some relevant code samples and explanations.

Quick background of my problem. I'm working with the Rotten Tomatoes API for my app, and am using GSON for the parsing of their data. I was initially targeting 2.3, and this worked fine. Then I decided to have support for ICS, and of course ran into the "no network operation on the UI thread" - so I started to delve into AsyncTask.

Here is my InputStream method:

private InputStream retrieveStream(String url) {

DefaultHttpClient client = new DefaultHttpClient();

HttpGet getRequest = new HttpGet(url);

try {

HttpResponse getResponse = client.execute(getRequest);
final int statusCode = getResponse.getStatusLine().getStatusCode();

if (statusCode != HttpStatus.SC_OK) {
Log.w(getClass().getSimpleName(),
"Error " + statusCode + " for URL " + url);
return null;
}

HttpEntity getResponseEntity = getResponse.getEntity();
return getResponseEntity.getContent();
}
catch (IOException e) {
getRequest.abort();
Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
}

return null;
}


Which was working fine in my main activity, and now is giving me issues when trying to 'convert' it into AsyncTask. I've been calling it like this:

InputStream source = retrieveStream( url parameter );


Then I tried moving that method into my AsyncTask class, and calling it like this:

private PerformMovieSearch performSearch = new PerformMovieSearch(this);
InputStream source = performSearch.retrieveStream(movieQueryUrl);


But that doesn't cut it, still get the error about performing network actions on the UI. What I need to figure out is how to call 'retrieveStream' from the AsyncTask I guess. Currently that class looks like this:

package net.neonlotus.ucritic;

[imports]

public class PerformMovieSearch extends AsyncTask<String, Void, String> {

private final Context context;
private ProgressDialog progressDialog;


public PerformMovieSearch(Context context){
this.context = context;
}


@Override
protected String doInBackground(String... urls) {
retrieveStream(urls[0]);

return null;

}

@Override
protected void onPreExecute() {
progressDialog= ProgressDialog.show(context, "Please Wait","Searching movies", true);

}


@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progressDialog.dismiss();

MyActivity.mListAdapter.notifyDataSetChanged();
}

public InputStream retrieveStream(String url) {

DefaultHttpClient client = new DefaultHttpClient();
HttpGet getRequest = new HttpGet(url);

try {
HttpResponse getResponse = client.execute(getRequest);
final int statusCode = getResponse.getStatusLine().getStatusCode();

if (statusCode != HttpStatus.SC_OK) {
Log.w(getClass().getSimpleName(),
"Error " + statusCode + " for URL " + url);
return null;
}

HttpEntity getResponseEntity = getResponse.getEntity();
return getResponseEntity.getContent();
} catch (IOException e) {
getRequest.abort();
Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
}

return null;
}


}

The "doinbackground" is what needs to be changed... but I can't seem to find a straight way to get that working properly. I was executing using

new PerformMovieSearch(this).execute(movieQueryUrl);


I know that is a lot of stuff, potentially confusing... but if anybody knows how to essentially do the retrieveStream method asynchronously, that would be great. Like I said, Ive tried many things, did plenty of research, just could not come up with anything useful.

Answer

the point is, you didn't understand how asynctask works!

You MUST read the guide Processes and Threads: http://developer.android.com/guide/components/processes-and-threads.html

But ok, let me try help you.

On doInBackground you are correctly calling the method retrieveStream, but you are doing nothing with the stream. So, you have to process the stream and then, return it. As you said you are expecting an JSON, I'm assuming you will receive a String, so the code of your retrieveStream should like this:

public String retrieveStream(String url) {

    DefaultHttpClient client = new DefaultHttpClient();
    HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse getResponse = client.execute(getRequest);
        final int statusCode = getResponse.getStatusLine().getStatusCode();

        if (statusCode != HttpStatus.SC_OK) {
            Log.w(getClass().getSimpleName(),
                    "Error " + statusCode + " for URL " + url);
            return null;
        }

        HttpEntity getResponseEntity = getResponse.getEntity();
        String jsonString = EntityUtils.toString(getResponseEntity);
        return jsonString;
    } catch (IOException e) {
        getRequest.abort();
        Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
    }

    return null;
}

Look that I changed the return type to String. And maybe, you should change the name to retrieveMoviesJSON or something like this.

And you should change your AsyncTask to something like this:

class PerformMovieSearch AsyncTask<String, Void, ArrayList<Movie>>() {

            @Override
            protected void onPreExecute() {
                progressDialog= ProgressDialog.show(context, "Please Wait","Searching movies", true);
            }

            @Override
            protected ArrayList<Movie> doInBackground(String... params) {
                String moviesJson = retrieveStream[params[0]];
                JSONObject moviesJson = new JSONObject(moviesJson);
                ArrayList<Movie> movies = new ArrayList<Movie>();
                /*
                 * Do your code to process the JSON and create an ArrayList of films.
                 * It's just a suggestion how to store the data.
                 */
                return movies;
            }

            protected void onPostExecute(ArrayList<Movie> result) {
                progressDialog.dismiss();
                //create a method to set an ArrayList in your adapter and set it here.
                MyActivity.mListAdapter.setMovies(result);
                MyActivity.mListAdapter.notifyDataSetChanged();
            }
        }

And you can call as the same way you were doing.

Is it clear? Need more explanation?

[]s Neto