Android AsyncTask (REST client): A source code example

I just got back into using an Android AsyncTask, and it took a little while to re-load the concepts in my head. I used AsyncTask’s a few years ago, but haven’t used them since.

To help remember how they work, I created a little AsyncTask example project, and I’ve included all of the source code for that project here. I’ll show all of the source code for my classes and configuration files, and then explain the code at the end.

Update: By popular request I’ve put this project on Github so you can use the code more easily in your project. The Github URL is shown at the end of the article.

Update: I just found this definition of an AsyncTask in the Android docs, and it’s excellent: “An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread.” That perfectly describes why you want/need to use an AsyncTask; you need to run something that’s going to take a little while to run, and when it’s finished, you’ll want to update the UI with its result.

My Activity

I only have one Activity, which I named TestActivity:

package com.alvinalexander.asynctasks;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;

public class TestActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            // if the screen is in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            TestFragment trendsFragment = new TestFragment();
            getFragmentManager().beginTransaction().add(android.R.id.content, trendsFragment).commit();
        }
    }
}

My Fragment

I also only have one Fragment, and named it TestFragment:

package com.alvinalexander.asynctasks;

import java.net.URI;
import org.apache.http.client.methods.HttpGet;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class TestFragment extends Fragment {
    
    private static final String TAG = "AATestFragment";

    // you'll want to call a REST service, but for basic network testing i use any url
    //private static final String TEST_URL                 = "http://jsonplaceholder.typicode.com/comments";
    private static final String TEST_URL                   = "http://denverpost.com/sports";
    private static final String ACTION_FOR_INTENT_CALLBACK = "THIS_IS_A_UNIQUE_KEY_WE_USE_TO_COMMUNICATE";

    ProgressDialog progress;
    private TextView ourTextView;

    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_test, container, false);
    }

    /**
     * Any code to access activity fields must be handled in this method.
     */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ourTextView = (TextView)getActivity().findViewById(R.id.myTextView);
        getContent();
    }

    private void getContent()
    {
        // the request
        try
        {
            HttpGet httpGet = new HttpGet(new URI(TEST_URL));
            RestTask task = new RestTask(getActivity(), ACTION_FOR_INTENT_CALLBACK);
            task.execute(httpGet);
            progress = ProgressDialog.show(getActivity(), "Getting Data ...", "Waiting For Results...", true);
        }
        catch (Exception e)
        {
            Log.e(TAG, e.getMessage());
        }

    }

    @Override
    public void onResume()
    {
        super.onResume();
        getActivity().registerReceiver(receiver, new IntentFilter(ACTION_FOR_INTENT_CALLBACK));
    }

    @Override
    public void onPause()
    {
        super.onPause();
        getActivity().unregisterReceiver(receiver);
    }


    /**
     * Our Broadcast Receiver. We get notified that the data is ready this way. 
     */
    private BroadcastReceiver receiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            // clear the progress indicator
            if (progress != null)
            {
                progress.dismiss();
            }
            String response = intent.getStringExtra(RestTask.HTTP_RESPONSE);
            ourTextView.setText(response);
            Log.i(TAG, "RESPONSE = " + response);
            //
            // my old json code was here. this is where you will parse it.
            //
        }
    };

}

My “view” file

My view/layout file is named res/layout/fragment_test.xml, and contains this XML code:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/myScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >

    <TextView
        android:id="@+id/myTextView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/default_text"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</ScrollView>

My “strings” file

My strings file is named res/values/strings.xml, and contains this data in XML format:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">REST Tester</string>
    <string name="default_text">This is a test fragment</string>

</resources>

The RestTask class

The RestTask class comes from an older version of a book titled, Android Recipes, and with only a few modifications for logging, it looks like this:

package com.alvinalexander.asynctasks;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;

/**
 * Android RestTask (REST) from the Android Recipes book.
 */
public class RestTask extends AsyncTask<HttpUriRequest, Void, String>
{
    private static final String TAG = "AARestTask";
    public static final String HTTP_RESPONSE = "httpResponse";

    private Context mContext;
    private HttpClient mClient;
    private String mAction;

    public RestTask(Context context, String action)
    {
        mContext = context;
        mAction = action;
        mClient = new DefaultHttpClient();
    }

    public RestTask(Context context, String action, HttpClient client)
    {
        mContext = context;
        mAction = action;
        mClient = client;
    }

    @Override
    protected String doInBackground(HttpUriRequest... params)
    {
        try
        {
            HttpUriRequest request = params[0];
            HttpResponse serverResponse = mClient.execute(request);
            BasicResponseHandler handler = new BasicResponseHandler();
            return handler.handleResponse(serverResponse);
        }
        catch (Exception e)
        {
            // TODO handle this properly
            e.printStackTrace();
            return "";
        }
    }

    /**
     * `onPostExecute` is run after `doInBackground`, and it's
     * run on the main/ui thread, so you it's safe to update ui
     * components from it. (this is the correct way to update ui
     * components.)
     */
    @Override
    protected void onPostExecute(String result)
    {
        Log.i(TAG, "RESULT = " + result);
        Intent intent = new Intent(mAction);
        intent.putExtra(HTTP_RESPONSE, result);

        // broadcast the completion
        mContext.sendBroadcast(intent);
    }

}

The Android manifest file

The Android manifest file is named AndroidManifest.xml, and it looks like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.alvinalexander.asynctasks" >

    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".TestActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

The android.permission.INTERNET part of that file is probably the most important nugget.

How the AsyncTask example code works

The source code for my little AsyncTask example works like this:

  1. The AndroidManifest.xml file says the app start with the TestActivity class
  2. That activity class basically just loads the TestFragment
  3. The fragment gets a reference to the layout’s TextView, then calls getContent()
  4. That method starts the RestTask, and shows a ProgressDialog
  5. The RestTask doInBackground method runs
  6. When the RestTask is finished, the onPostExecute method is executed
  7. That method sends a BroadcastIntent
  8. The BroadcastIntent and BroadcastReceiver use the same name/label, so the BroadcastReceiver in the TestFragment knows that the BroadcastIntent is meant for it
  9. The BroadcastReceiver gets the data, and puts it in the TextView

As a result of how this code is currently written, I get to see the HTML data for my current TEST_URL.

Note: It’s been a long time since I wrote most of that code -- I just copied and pasted most of it into my small example project -- so a lot of it may have come from the Android Recipes book. (I just linked to the new version of that book, which was released in late January, 2015.)

AsyncTask Notes

Whenever I get away from Android and then come back to it, I have to remember how AsyncTask works. Today (July, 2017) I found that these quotes about the AsyncTask that may make it easier to understand:

  • An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread.
  • AsyncTask creates a background thread for you, and runs the code in the doInBackground method on that thread.
  • onPostExecute is run after doInBackground, and it's run on the main/ui thread, so you it's safe to update ui components from it. (this is the correct way to update ui components.)

The first quote comes from the official Android docs, and the last two quotes come from the book, Android Programming: The Big Nerd Ranch Guide.

The source code

I just made the source code for this project available at this Github URL.

Summary

I’ll update this code more if/when I think of things to add, but until then, if you wanted to see an Android AsyncTask example, I hope this code is helpful.