Handlers and AsyncTasks, Android doing multithreading

Multithreading is always tricky. On any platform and in any language, when not implemented properly you will get unexpected behavior sooner or later.

In the process of making an Android application suitable for Honeycomb, a colleague of mine ran into the NetworkOnMainThreadException, which is new in Honeycomb. On earlier Android versions, doing heavy operations on the main thread was strongly discouraged, but still possible. Now, the OS is forcing the developer to implement a proper application structure. Which isn’t a bad thing at all.

So, to solve the NetworkOnMainThreadException, network and other heavy operations needed to move to a background thread. Considering the structure of the application, the most logical choice was to use the Handler in combination with a Thread.

Basic setup HandlerThread

Here is an example:

public class MBOutcomeHandlerThread extends Thread
{
  public MBOutcomeHandlerThread(String name)
  {
    super(name);
  }

  private MBOutcomeHandler _outcomeHandler;

  @Override
  public void run()
  {
    try
    {
      Looper.prepare();

      _outcomeHandler = new MBOutcomeHandler();

      Looper.loop();
    }
    catch (Throwable e)
    {
      Log.e("Log", "Thread stopped due to error", e);
    }
  }

  public MBOutcomeHandler getOutcomeHandler()
  {
    return _outcomeHandler;
  }
}

Looper.prepare creates a new Looper instance for this thread, which then creates a new MessageQueue that Handlers can use to post messages on.

MBOutcomeHandler is a subclass of Handler, which implements the handleMessage method. The constructor of the Handler class associates with the Looper.

Looper.loop starts the Looper and keeps the thread alive. The Looper can be stopped by invoking Looper.quit. This will end run method of the Thread, hence, the Thread is finished.

It is also possible to use the HandlerThread class. This class does the Looper setup for you. It doesn’t really matter which one you choose, as long as you don’t forget to instantiate the Handler inside the run method. When using the HandlerThread you must instantiate the Handler in the onLooperPrepared method.

public class MBOutcomeHandlerThread extends HandlerThread
{
  private MBOutcomeHandler _outcomeHandler;

  public MBOutcomeHandlerThread(String name)
  {
    super(name);
  }

  @Override
  public void run()
  {
    super.run();
    _outcomeHandler = null;
  }

  @Override
  protected void onLooperPrepared()
  {
    _outcomeHandler = new MBOutcomeHandler();
  }

  public MBOutcomeHandler getOutcomeHandler()
  {
    return _outcomeHandler;
  }
}

Now that we have a HandlerThread running, we can use the Handler to send messages to the Looper.

Please be aware, that the HandlerThread keeps looping until the quit method is invoked. It doesn’t really matter if you use the instance of the HandlerThread, the Handler or the Looper. After the quit method is invoked, the HandlerThread is finished and cannot be started again. For example, if the HandlerThread has been quit because the Activity was pushed to the background, you need to clean up the HandlerThread in an onPause or onStop. When the Activity comes to the foreground again, a new HandlerThread must be created. Of course, the starting and stopping of the HandlerThread depends on what kind of operations are performed in the Handler. Then again, I think it is good practice to do a little clean up, instead of Android doing all the work.

AsyncTasks

After the new Handler was created, the next problem occurred. After pushing the application to the background and let it come to the foreground again, the application crashed because of the following error: Handler{4062bc78} sending message to a Handler on a dead thread. Because I was implementing a Handler, I assumed the exception had something to do with the Handler I created. After a lot of searching and debugging, the cause of the exception were the AsyncTasks that are used inside the Handler. Then, after reading the documentation of AsyncTask, Android is very clear that AsyncTasks must be created and started on the UI thread. But, in the process of moving code to the handler, that was overlooked. After creating and starting the AsyncTasks, everything worked fine!

Recap

HandlerThreads and Handlers are a great way to handle heavy or long during operations on a separate thread. When using AsyncTasks in your Handler, make sure that they’re created on the UI thread.

Leave a Reply

*

captcha *