Smartphones

Using Android's AsyncTask to handle long-running I/O

Long-running I/O is one of the keys to managing state in Android apps. William Francis describes how to use an extension of AsyncTask to handle long-running I/O.

Tutorial on how to use AsyncTask to handle long-running I/O

1. Begin a new project in Eclipse targeting Android SDK 1.6 or greater.

2. We need a simple XML layout that consists of a couple text labels and a button.

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="8dip"
android:text="Long Running I/O Demo"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Begin"
android:layout_gravity="center"
android:layout_margin="12dip"
android:id="@+id/begin_button"/>
<TextView
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="8dip"/>
</LinearLayout>

3. We will move to our /src folder and modify the Main.java file. Let's start by implementing a barebones on create override.

Main.java
package com.authorwjf;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Main extends Activity implements OnClickListener{
private LongRunningIO mLongRunningIO = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button)findViewById(R.id.begin_button);
b.setOnClickListener(this);
}
}

4. Eclipse doesn't like that private variable because it can't resolve the class LongRunningIO -- it doesn't exist. We are going to create it statically within our Main.class. If you aren't familiar with AsyncTask, our extension has several functions you might wonder about. It's worthwhile to read the official documentation for the AsyncTask class. Here are brief highlights about some of the methods:

  • doInBackground() is the worker method that gets called on the background thread when execute() is invoked on an instance of AysncTask. For the purpose of the example, this function does nothing but count to ten.
  • OnProgressUpdate() gets executed on the UI thread whenever publishProgress() is called on the background thread.
  • isDone() returns a flag indicating whether an AsyncTask has completed. This is so important because the OS only lets us execute an AsyncTask one time. If we need to run it again, the first one must be destroyed and a new one created.
  • attach() and detach() are used to register and unregister the reference to an activity's UI.
Main.java
static class LongRunningIO extends AsyncTask <Void, Integer, Void> {
private Activity mActivity = null;
private boolean mDone = false;
boolean isDone() { return mDone; }
@Override
protected Void doInBackground(Void... params) {
for (int i = 1; i<11; i++) {
SystemClock.sleep(1000);
if (mActivity!= null) {
publishProgress(i);
}
}
mDone = true;
return null;
}
@Override
protected void onProgressUpdate(Integer... progress) {
if (mActivity != null) {
TextView tv = (TextView)mActivity.findViewById(R.id.status_text);
tv.setText(Integer.toString(progress[0])+"/10");
}
}
void attach(Activity a) {
mActivity = a;
}
void detach() {
mActivity = null;
}
}

5. With the static inner class in place, we can go back and modify the on create, as well as add the on click handler. The theory is we want to look at the last non configuration instance and if there is already a long-running I/O task executing we just attach to it. We take a similar approach in the on click handler, except in that case we also need to handle the case when the user clicks the button after the AsyncTask has already completed.

Main.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button)findViewById(R.id.begin_button);
b.setOnClickListener(this);
mLongRunningIO = (LongRunningIO)getLastNonConfigurationInstance();
if (mLongRunningIO != null) {
TextView tv = (TextView)findViewById(R.id.status_text);
if (mLongRunningIO.isDone()) {
tv.setText("10/10");
} else {
tv.setText("Re-initializing");
}
mLongRunningIO.attach(this);
}
}
@Override
public void onClick(View v) {
boolean needToCreate = false;
if (mLongRunningIO == null) {
needToCreate=true;
} else {
if (mLongRunningIO.isDone()) {
mLongRunningIO.detach();
mLongRunningIO = null;
needToCreate=true;
}
}
if (needToCreate) {
mLongRunningIO = new LongRunningIO();
mLongRunningIO.attach(this);
mLongRunningIO.execute();
}
}

6. You'll want to override the on retain non instance configuration, as this is how we pass the asynchronous task from activity to activity. It's important to call detach, or you will "leak" your activity, effectively preventing the garbage collector from ever being able to reclaim the memory.

Main.java
@Override
public Object onRetainNonConfigurationInstance() {
if (mLongRunningIO != null) {
mLongRunningIO.detach();
}
return(mLongRunningIO);
}
That's it! Unlike a lot of the sample programs we've done in the App Builder blog, you really need to run this one a few times to get a feel for what is happening. Load the APK to a phone or emulator, click the Begin button, and then observe the counter (Figure A). Try rotating the phone while the count is ticking and see what happens. Figure A

As I said when we began this tutorial, managing state on the Android platform can be a tricky business. Hopefully you'll find the code in this post helps to demystify long-running I/O and adds some handy techniques to your digital toolbox.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

0 comments

Editor's Picks