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:
- The AndroidManifest.xml file says the app start with the
TestActivity
class - That activity class basically just loads the
TestFragment
- The fragment gets a reference to the layout’s
TextView
, then callsgetContent()
- That method starts the
RestTask
, and shows aProgressDialog
- The RestTask
doInBackground
method runs - When the RestTask is finished, the
onPostExecute
method is executed - That method sends a
BroadcastIntent
- The BroadcastIntent and
BroadcastReceiver
use the same name/label, so theBroadcastReceiver
in theTestFragment
knows that theBroadcastIntent
is meant for it - The
BroadcastReceiver
gets the data, and puts it in theTextView
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 thedoInBackground
method on that thread.onPostExecute
is run afterdoInBackground
, 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.