Android HttpURLConnection with AsyncTask Tutorial

This tutorial will show how to complete an HTTP POST using Java’s HttpURLConnection library and Android’s AsyncTask library. I will discuss these libraries assuming that you already know what asynchronous means and how HTTP works.

This tutorial will be very code heavy, with explanations given mainly in the comments of the code.


First we create a class that extends AsyncTask. AsyncTask has 3 generics that are set when you extend it. They describe <parameters, progress, result>. This means that you can pass things into the task with parameters, monitor the progress of the task with progress, and get a result back from the task using result.

public class HttpPostAsyncTask extends AsyncTask<String, Void, Void> {
...

I have set the first generic to be of type String and will use that to set the URL for the request.

I personally set the last two types to Void because I call my own custom callback when the HTTP request is complete and I don’t have any need for monitoring the progress of the tasks.

public class HttpPostAsyncTask extends AsyncTask<String, Void, Void> {
    // This is the JSON body of the post
JSONObject postData;
// This is a constructor that allows you to pass in the JSON body
public HttpPostAsyncTask(Map<String, String> postData) {
if (postData != null) {
this.postData = new JSONObject(postData);
}
}

// This is a function that we are overriding from AsyncTask. It takes Strings as parameters because that is what we defined for the parameters of our async task
@Override
protected Void doInBackground(String... params) {

try {
// This is getting the url from the string we passed in
URL url = new URL(params[0]);

// Create the urlConnection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();


urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);

urlConnection.setRequestProperty("Content-Type", "application/json");

urlConnection.setRequestMethod("POST");


// OPTIONAL - Sets an authorization header
urlConnection.setRequestProperty("Authorization", "someAuthString");

// Send the post body
if (this.postData != null) {
OutputStreamWriter writer = new OutputStreamWriter(urlConnection.getOutputStream());
writer.write(postData.toString());
writer.flush();
}

int statusCode = urlConnection.getResponseCode();

if (statusCode == 200) {

InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream());

String response = convertInputStreamToString(inputStream);

// From here you can convert the string to JSON with whatever JSON parser you like to use
               // After converting the string to JSON, I call my custom callback. You can follow this process too, or you can implement the onPostExecute(Result) method
            } else {
// Status code is not 200
// Do something to handle the error

}

} catch (Exception e) {
Log.d(TAG, e.getLocalizedMessage());
}
return null;
}
}

Now to execute a POST using this class we just made, do the following:

Map<String, String> postData = new HashMap<>();
postData.put("param1", param1);
postData.put("anotherParam", anotherParam);
PostTask task = new HttpPostAsyncTask(postData);
task.execute(baseUrl + "/some/path/goes/here");

Note if using your own callback

If you are using a custom callback of some kind, you will need to get the UI thread before performing UI updates of any kind. You can do this as follows:

getActivity().runOnUiThread(new Runnable() {
public void run() {
// UI updates here
}
});

If you use the onPostExecute(Result) method instead, it is already executed on the UI thread.


Hopefully this helps understand how to use HttpURLConnection and AsyncTask a little better. If you are still perplexed about the custom callback I was talking about throughout the article, here is a little explanation.

The reason that I use a custom callback is because it makes it so I can use the same class to perform all POSTs for my entire app. If you use the standard call back, you will have to write a separate class the extends AsyncTask for every single POST that you want to do. This isn’t necessarily a bad thing, but I decided to make it “simpler.”

Custom Callback

First, create an enumeration to keep track of what type of request you are executing.

public enum RequestType {
REQUEST_TYPE_1,
REQUEST_TYPE_2
}

Next, create an interface that is used for the callback itself:

public interface CustomCallback {
// This function will be called from inside of your AsyncTask when you are ready to callback to your controllers (like a fragment, for example)
// The object in the completionHandler will be whatever it is that you need to send to your controllers


void completionHandler(Boolean success, RequestType type, Object object);
}

Now that there is a callback to use, we need to adjust our HttpPostAsyncTask to use it:

private class HttpPostAsyncTask extends AsyncTask<String, Void, Void> {
...
    // First change the constructor to accept something that implements our interface
    public HttpPostAsyncTask(Map<String, String> postData, RequestType type, CustomCallback callback) {
// Also create class level variables for type and callback
this.type = type;
this.callback = callback;
...
}
    // Second when you get your response back from the HTTP POST:
    ...
String response = convertInputStreamToString(inputStream);
    switch (type) {
case REQUEST_TYPE_1:
// Use the response to create the object you need
callback.completionHandler(true, type, someObjectYouNeed)
break;
case REQUEST_TYPE_2:
// Do something
break;
default:
break;
}
...
}

Now, inside of your activity or fragment where you want to get a callback do the following:

public class SomeFragment extends Fragment implements CustomCallback {
...

// Here is an example of calling HttpPostAsyncTask with our changes
public someFunction() {
HttpPostAsyncTask task = new HttpPostAsyncTask(postData, this, RequestType.REQUEST_TYPE_1);
        task.execute( baseUrl + "/some/path/goes/here");
}
...
@Override
public void completionHandler(Boolean success, RequestType type, Object object) {
switch (type) {
case REQUEST_TYPE_1:
// Do UI updates ON THE UI THREAD needed for response to REQUEST_TYPE_1 using the object that sent here
break;
case REQUEST_TYPE_2:
// Do something
break;
default: break;
}
}
}

If you made it this far, I hope that everything made sense. If you have any questions or spot any mistakes please comment and let me know.

Update:

I realized I forgot to provide the following function:

private String convertInputStreamToString(InputStream inputStream) { 
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
StringBuilder sb = new StringBuilder();
String line;
try {
while((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}