Chapter 3. Distributing Work with Handler and HandlerThread

In Chapter 2, Staying Responsive with AsyncTask, we familiarized ourselves with the most well-known concurrency construct of the Android platform. What is perhaps less well known are the mechanics of how AsyncTask coordinates work between background threads and the main thread.

In this chapter we'll meet some of the lower-level constructs that AsyncTask builds on to get its work done.

We'll see how to defer tasks to happen in the future on the main thread, whether that is as soon as possible, after a specified delay, or at a specified time, and we'll apply the same concepts to scheduling work on background threads and coordinating the results with the main thread.

In this chapter we will cover the following topics:

  • Understanding Looper
  • Building responsive apps with Handler
  • Scheduling work with post
  • Canceling a pending Runnable
  • Scheduling work with send
  • Canceling pending Messages
  • Multithreaded programming with Handler
  • Building responsive apps with HandlerThread
  • Handler programming issues
  • Applications of Handler and HandlerThread

Understanding Looper

Before we can understand Handler, we need to meet the aptly named Looper. Looper is a simple class that quite literally loops forever, waiting for Messages to be added to its queue and dispatching them to target Handlers. It is an implementation of a common UI programming concept known an Event Loop.

To set up a Looper thread, we need to invoke two static methods of Looperprepare and loop—from within the thread that will handle the message loop.

class SimpleLooper extends Thread {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}

That was easy; however, the SimpleLooper class defined here provides no way to add messages to its queue, which is where Handler comes in.

Handler serves two purposes—to provide an interface to submit Messages to its Looper queue and to implement the callback for processing those Messages when they are dispatched by the Looper.

To attach a Handler to SimpleLooper, we need to instantiate the Handler from within the same thread that prepared the Looper. The Handler is associated with the main Looper by the super-class constructor, which obtains the Looper using a static method.

class SimpleLooper extends Thread {
    public Handler handler;
    public void run() {
        Looper.prepare();
        handler = new Handler();
        Looper.loop();
    }
}

Once started, the Looper thread will wait (Object.wait) inside Looper.loop() for Messages to be added to its queue.

When another thread adds a Message to the queue it will notify (Object.notify) the waiting thread, which will then dispatch the Message to its target Handler by invoking the Handler's dispatchMessage method.

The wait/notify mechanism is a very efficient way of suspending activity on a particular thread while there is no work for it to do, so that it doesn't waste CPU cycles, then resuming work once there is something for it to do.

Because dispatchMessage is invoked by the thread running the message loop, we can send Messages to the Handler from any thread and they will always be dispatched by the message loop thread as shown in the following diagram:

Understanding Looper

We already saw that we can create our own Looper threads, but here's the fun part and this may come as a surprise: the main thread is in fact a Looper thread, as we can see in the following stack trace:

at example.handler.MyActivity.onCreate(Example1Activity.java:19)
at android.app.Activity.performCreate(Activity.java:5206)
…
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)

That's right! Everything that happens in an Activity lifecycle callback method is running in a dispatchMessage call invoked by the main Looper!

The interesting thing to realize here is that we can send messages to the main thread from any other thread (or even from the main thread itself) and in doing so, hand over work from background threads to the main thread—for example, to have it update the user interface with the results of background processing.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.29.146