AsyncTask Missteps

AsyncTask comes up in conversation quite often around the office, usually because of a failed code review or some related bug that’s been discovered. It’s one of the most well understood classes on the Android platform, and yet it’s also one of the hardest classes to use correctly. Even advanced Android developers get it wrong.

In the following discussion, we’ll take a look at a few snippets of code, touch on some of their problems, and arrive (hopefully) at a pattern of AsyncTask usage that’s suitable for most cases.

Let’s start with a fairly typical example.

public class MyActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Find views and assign them to member variables.
 
        new AsyncTask<void, void,="" string="">()
        {            
            @Override
            protected String doInBackground(Void... params)
            {
                // Do some long running task.
                return result;
            }
 
            @Override
            protected void onPostExecute(String result)
            {
                // Update UI
            }
        }.execute();
    }
    // Various View member variables.
}
</void,>

This seems fairly straightforward. When our Activity is created, we launch an AsyncTask to perform some background work, which is performed in doInBackground. When the background work is finished, doInBackground returns a String which is used by onPostExecute to safely update the UI.

We’re using the AsyncTask here exactly as I’ve seen it used in production code. UI elements are being updated safely. Work is being done in the background. We’re in no danger of an ANR message. So what exactly is the problem here?

The problem isn’t what happens inside the AsyncTask. The problem is what happens outside of it. Specifically, what happens if the Activity is destroyed while the AsyncTask is running?

Imagine this scenario – the Activity is created, the AsyncTask is instantiated and executed, and then the screen orientation changes. When screen orientation changes under normal circumstances, the original Activity is destroyed and a new one is created. This means another AsyncTask will be instantiated and the background work will start all over again.

But that’s not the worst of it. The original AsyncTask will continue to run until it’s finished. And once it’s finished, it’ll update UI elements that aren’t visible anymore. And because the original AsyncTask still has an implicit outerclass pointer to the original Activity, the original Activity can’t be garbage collected until the original AsyncTask is finished. Change the screen orientation a bunch of times on a resource intensive Activity and you risk running out of memory.

There’s also a limit on the number of AsyncTasks you can have running at once. For Activities with smaller footprints, you’ll most likely run out of AsyncTasks before you start to see memory problems appear.

Does that make you uncomfortable? It certainly should.

So how do we work around these issues? Let’s address these one at a time, starting with the simplest – the implicit pointer to the outer class. This can be solved by making the AsyncTask class static, which also means it can no longer be anonymous.

Removing Implicit Outerclass Pointers

public class MyActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Find views and assign them to member variables.
 
        MyTask task = new MyTask();
        task.execute();
    }
 
    static class MyTask extends AsyncTask<void, void,="" string="">
    {
        @Override
        protected String doInBackground(Void... params)
        {
            // Do some long running task.            
            return result;
        }
 
        @Override
        protected void onPostExecute(String result)
        {
            // Need to update UI. But how?
        }
    }    
    // Various View member variables.
}
</void,>

So far so good. Our AsyncTask will no longer keep a reference to the Activity. But now we can’t update the UI when the task completes, which kind of defeats the purpose. How do we fix this? By giving back an Activity reference to the AsyncTask. “Wait, isn’t that what we tried to eliminate by making the class static?” Well, yes. But we wanted to remove the implicit reference, which is something we couldn’t change at runtime. With something a bit more explicit, we have direct control over when the reference comes and goes.

public class MyActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Find views and assign them to member variables.
 
        m_task = new MyTask();
        m_task.m_activity = this;
        m_task.execute();
    }
 
    @Override
    public void onDestroy()
    {
        super.onDestroy();
        m_task.m_activity = null;
    }
 
    static class MyTask extends AsyncTask<void, void,="" string="">
    {
        @Override
        protected String doInBackground(Void... params)
        {
            // Do some long running task.            
            return result;
        }
 
        @Override
        protected void onPostExecute(String result)
        {
            if (m_activity != null)
            {
                // Update UI
            }
        }
 
        MyActivity m_activity = null;
    }
 
    private MyTask m_task = null;    
    // Various View member variables.
}
</void,>

That’s better. In the above example we remove the Activity reference in the AsyncTask when the Activity is destroyed. The AsyncTask can continue to run without preventing our Activity from being garbage collected.

Concurrent AsyncTask Limitations

Now let’s briefly discuss the problem of concurrent AsyncTasks. If you weren’t aware that there’s an upper limit to concurrent AsyncTasks, Google “AsyncTask limits” and you’ll find plenty of information about it. We need to control the lifetime of our AsyncTask instance. What that boils down to is canceling the task and letting the garbage collector deal with it. Canceling a task is a two-step process. First, we set the AsyncTask’s cancelled flag in the Activity’s onDestroy method.

m_task.cancel(false);

This alone doesn’t actually cancel the task. It has no effect on the background process if the task chooses to ignore it. So we also need to regularly check the return value of isCancelled() inside of our AsyncTask’s doInBackground method and bail out appropriately. This allows us to terminate that background process cleanly.

if (isCancelled())
{
    return null;
}

Notice that we passed false to the AsyncTask’s cancel method. If you pass true, it interrupts the thread and can leave things in a particular awkward state. See the Thread class’s interrupt method for details.

One AsyncTask To Rule Them All

We still have a problem of a new AsyncTask being created and executed every time our Activity is destroyed and recreated. In most cases, creating a new AsyncTask instance is unnecessary. We often only need the AsyncTask to query a database, make a web service request, or read some file once. So it’d be nice if we could leverage a single AsyncTask no matter how many times our Activity is temporarily destroyed/created.

I’ve seen some folks set the android:configChanges attribute in the Activity’s XML declaration inside the app manifest just to avoid the redundant AsyncTask issue. And while it’s certainly doable, it’s somewhat of an obscure solution. android:configChanges is meant to solve other problems. That approach may confuse other developers who will need to maintain your code. It also makes the UI a bit inflexible because the default behavior may actually be behavior you want.

I’ve also seen some folks address the issue using invisible Fragments. Again, it works. But it’s somewhat obscure if you’re using a Fragment oriented approach just to solve this issue. Make sure your Fragment usage is appropriate for your UI design. Never use a sledgehammer when a rubber mallet will do.

The approach I like best is mentioned in this StackOverflow thread and in Hervé Guihot’s book “Pro Android Apps Performance Optimization”.

public class MyActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Find views and assign them to member variables.
 
        m_task = (MyTask) getLastNonConfigurationInstance();
        if (m_task != null)
        {
            m_task.m_activity = this;
        }
        else
        {
            m_task = new MyTask();
            m_task.m_activity = this;
            m_task.execute();
        }
    }
 
    @Override
    public void onDestroy()
    {
        super.onDestroy();
 
        m_task.m_activity = null;
 
        if (this.isFinishing())
            m_task.cancel(false);
    }
 
    @Override
    public Object onRetainNonConfigurationInstance() 
    {
        return m_task;
    }
 
 
    static class MyTask extends AsyncTask<void, void,="" string="">
    {
        @Override
        protected String doInBackground(Void... params)
        {
            // Do some long running task. We need to make sure
            // we peridically check the return value of isCancelled().
            return result;
        }
 
        @Override
        protected void onPostExecute(String result)
        {
            if (m_activity != null)
            {
                // Update UI
            }
        }
 
        MyActivity m_activity = null;
    }
 
    private MyTask m_task = null;
 
    // Various View member variables.
}
</void,>

The key piece here is the use of the onRetainNonConfigurationInstance and getLastNonConfigurationInstance methods to reuse an AsyncTask instance between build up and tear down of the Activity. Using this approach will always result in a single AsyncTask being created and used.

My code above differs a bit from that mentioned in the StackOverflow discussion and in Guihot’s book. One notable difference is that I check to see if the Activity is actually finishing in the onDestroy method. If it is, we cancel the task. isFinishing will return true if this Activity is being dismissed as a result of going backwards in the Activity stack.

Our Final Approach

There is still a bug here. Can you spot it? What happens if the Activity has been destroyed and recreated after the AsyncTask has completed? Our UI doesn’t get updated. The solution to this is fairly simple and illustrated in the final version of our Activity below.

public class MyActivity extends Activity 
{
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Find views and assign them to member variables.
 
        m_task = (MyTask) getLastNonConfigurationInstance();
        if (m_task != null)
        {
            m_task.m_activity = this;
            if (m_task.m_isFinished)
                m_task.updateUI();
        }
        else
        {
            m_task = new MyTask();
            m_task.m_activity = this;
            m_task.execute();
        }
    }
 
    @Override
    public void onDestroy()
    {
        super.onDestroy();
 
        m_task.m_activity = null;
 
        if (this.isFinishing())
            m_task.cancel(false);
    }
 
    @Override
    public Object onRetainNonConfigurationInstance() 
    {
        return m_task;
    }
 
 
    static class MyTask extends AsyncTask<void, void,="" string="">
    {
        @Override
        protected String doInBackground(Void... params)
        {
            // Do some long running task. We need to make sure
            // we peridically check the return value of isCancelled().
            return result;
        }
 
        @Override
        protected void onPostExecute(String result)
        {
            m_result = result;
            m_isFinished = true;
            updateUI();
        }
 
        public void updateUI()
        {
            if (m_activity != null)
            {
                // Update UI using m_result
            }            
        }
 
        // These should never be accessed from within doInBackground()
        MyActivity m_activity = null;
        boolean m_isFinished  = false;
        String m_result = null;
    }
 
    private MyTask m_task = null;
 
    // Various View member variables.
}
</void,>

I don’t claim that this pattern is a one-sized fits all solution. But I think it covers most cases quite well. There are a few things I avoided discussing here, such as dealing with progress dialogs and how to design tasks that can be used by multiple activities. I didn’t want to add too much complexity to the examples. And I think those sorts of things should be fairly intuitive once you’ve achieved a certain degree of comfort with AsyncTask

Book Review: Pro Android Apps Performance Optimization

A few weeks ago, a copy of Hervé Guihot’s “Pro Android Apps Performance Optimization” landed on my doorstep. I had been waiting on this book to arrive for months. Having served as a technical reviewer for it back in the fall (disclaimer), I had been given a taste of what was to come. So it was nice to finally get to see it in its final form and to read the entire thing without the pressure of a deadline looming.

Most Android books on the market today provide developers with either a 50,000 foot view of the platform or focus on specific APIs that cater to a niche community such as game developers. “Pro Android Apps Performance Optimization” is a bit different. Its goal is to expand on what you already know and show you how to do things faster and with less memory – information of value to every type of Android project.

“Pro Android Apps Performance Optimization” consists of nine chapters, all of which are targeted at intermediate to advanced skill-level Android developers.

Chapter 1 – Optimizing Java Code – Many books include a “kitchen sink” chapter at the end to wrap up loose ends. Guihot, however, decided to put his right at the front. You’ll find a lot of different topics covered in this chapter, including basic algorithm optimization, Android-specific collection classes, fragmentation, and even SQLite query optimization. The chapter is shorter than I would have expected given the wide range of subject matter. But you’ll find lots of good optimization nuggets in here.

Chapter 2 – Getting Started With the NDK – This is your basic introduction to JNI and the NDK. You may be inclined to skip this chapter if you’ve worked with the NDK before. However, if you’ve never created an Activity or utilized a sensor in native code, I’d advise you not to pass it over. There’s a nice introduction to these sorts of things towards the end of the chapter.

Chapter 3 – Advanced NDK – This is the assembly language chapter, or more specifically the ARM assembly language chapter. There are brief snippets of x86 assembly scattered here and there, but the bulk of the discussion is focused on ARM. The author ends the chapter with a brief discussion on GCC extensions.

Chapter 4 – Using Memory Efficiently – The first half of the chapter is a common sense discussion regarding memory allocation and access optimization. The latter half discusses how to avoid memory leaks, describes the various Java reference types, and ends with a very brief discussion on the various classes and methods you can use to inquire about memory usage. One thing that Guihot mentions in passing but definitely warrants more attention is the Memory Analyzer tool. Download and learn it after reading this chapter. It’s one of the most valuable Android tools to have in your toolbox.

Chapter 5 – Multithreading and Synchronization – This chapter starts off with a quick discussion of Java threads and synchronization. It then introduces some of the Android-specific threading classes. The Java threading coverage here is very, very basic. And in Guihot’s defense, there have been entire books devoted to the topic. One such book, and my favorite, is Allen Holub’s “Taming Java Threads”. It’s a little dated, but it’s still very relevant. Check it out. The chapter ends with a discussion of something I love to ding people on in code reviews – the proper management of an ASyncTask from an Activity.

Chapter 6 – Benchmarking and Profiling – Measuring speed is the focus of this chapter. It begins with a discussion on brute forcing speed measurements and then moves on to using Traceview for profiling execution paths in both Java and native code. The chapter ends somewhat randomly with a discussion of LogCat.

Chapter 7 – Maximizing Battery Life – This chapter begins with a brief discussion of batteries. It explains how capacity is measured and demonstrates how you can find out all sorts of data regarding the state of a device’s battery. The meat of the chapter is devoted to strategies for reducing power consumption in your applications. The topics include better BroadcastReceiver management, network access etiquette, location services, and sensors. A discussion on Alarms and WakeLocks wrap up the chapter.

Chapter 8 – Graphics – I was a little disappointed by this part of the book and I think it was entirely due to the chapter’s title, “Graphics.” I expected to see some tips and tricks for working with Views and Canvases, maybe some general graphics related algorithm optimizations, and perhaps even some discussion on better handling of Drawables (a sometimes sneaky source of memory leaks). None of that is in here. The first half of the chapter is devoted to Activity layouts and layout optimization. The second half is devoted to OpenGL topics like mipmaps, texture compression, and shaders. Despite my disappointment, there’s a lot of good information in this chapter. So don’t get the wrong idea.

Chapter 9 – RenderScript – This chapter is more or less an introduction to RenderScript.

Overall, I think this book has tremendous value. Most of my criticisms revolve around the awkward organization of the book and the way the content flows. Once you get past that, you’ll undoubtedly find a lot of things in here that’ll change your approach to Android coding. The author’s style of writing is very casual. And coming in at 270 pages, which is kind of short for a programming book, it certainly won’t take you long to digest it.

Go snag a copy.

Confessions of a Non-Gamer

I haven’t been much of a gamer since I graduated college. Back then it was mostly Quake and Command and Conquer. There have been one or two games that captured my interest in the years since (Alice and Undying). But for the most part I’ve fallen out of touch with them.

For me, the last great game was Duke Nukem 3D. I have a lot of good memories attached to that game – learning to use a null modem cable, late nights spent trying to figure out the right AT commands for my modem so I could play against my friend Tuck, trying to figure out the optimal CONFIG.SYS and AUTOEXEC.BAT configuration to make the game playable on my old 386DX.

When Duke Nukem Forever finally came out back in June, I had to buy a copy. How could any Duke fan not, right? So I did. And I discovered that I pretty much loathe the modern day PC gaming experience. Why you ask?

1) Steam – I had no idea what this was before installing DNF. I was a little put off by having to create a Steam account during the game installation process. But I didn’t think it was such a big deal until I realized…

2) At least half the game content doesn’t come on the disk. The installer ended up downloading quite a sizeable chunk of the game over my broadband connection. Combine 1 and 2 together and I realized…

3) In 10 years, I probably won’t be able to install DNF. I can STILL install and play Duke Nukem 3D (using Dosbox). And it runs great. How am I going to recapture the magic of my 30’s when I’m 50?

4) You can’t play the game if Steam doesn’t want you to play it right now.

5) Goodbye impulse game playing. On a number of occasions, I found myself in the mood for some simulated carnage and was instead greeted with this.

After 7 minutes, I lost my patience and the desire to play altogether.

Bring back install disks with the full game. If the installer finds an update when it’s finished, make the update optional. Don’t require a network connection for a game that doesn’t need it (DNF Campaign mode). And, for the love of God, don’t make me sign up for a gaming service just to play a single player game.