When you first see the name of a HandlerThread, you might think of the two classes Handler and Thread. HandlerThread descends from Thread, which is essentially a Thread and is specifically used to process Handler messages.

Introduction to HandlerThread

Take a look at the official explanation:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

The looper object can be used to create a Handler class for scheduling, and the start() method must be called.

In Android development, people unfamiliar with multithreaded development might use new Thread(){… }.start(). This is essentially fine when there is only a single time-consuming task, but what if there are multiple time-consuming tasks that need to be executed sequentially? You don’t have to create and destroy threads multiple times, which can be costly in terms of system resources and performance issues. So, what’s the solution?

We can create a worker thread and loop through time-consuming tasks as follows:

Handler mHandler; private voidcreateWorkerThread() {  new Thread() {      @Override        public void run() {            super.run();            Looper.prepare();             mHandler = new Handler(Looper.myLooper()) {                @Override                public void handleMessage(Message msg) {                    ......                }            };            Looper.loop();        }    }.start();}Copy the code

In this worker thread:

  • call
    Looper.prepare()Create a Looper instance bound to the current thread.
  • Generate Handler instances using the Looper created above;
  • call
    Looper.loop()Implement message loop;

The asynchronous task is then looping through the Looper loop in the Handler’s handlerMessage(). This happens to be an implementation of HandlerThread.

@Overridepublic void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }Copy the code
  • HandlerThreadEssentially a Thread class that inherits Thread;
  • HandlerThreadHas its own internal Looper object through
    Looper.loop()for
    looperCycle;
  • By getting the HandlerThread
    looperThe object is passed to the Handler object, and then the
    handleMessage()Method to perform asynchronous tasks;
  • The HandlerThread must be called after it is created
    HandlerThread.start()Method to start the thread.

How to use HandlerThread

Create HandlerThread instance object
HandlerThread handlerThread = new HandlerThread("Handler Thread"); //HandlerThread handlerThread = new HandlerThread("Handler Thread", Process. THREAD_PRIORITY_DEFAULT);Copy the code

HandlerThread has two constructors by default that provide Settings for thread name and thread priority parameters.

Start the HandlerThread thread
handlerThread.start();Copy the code

The start() method is used to start a HandlerThread, which runs in a loop.

Build loop message handling mechanism with Handler
Handler workderHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {@override public Boolean handleMessage(Message MSG) {// Run in a worker thread (child thread) to implement its own Message processingreturn true; }});Copy the code

An asynchronous Handler object is constructed by passing the Looper object bound to HandlerThread to Handler as an argument. To implement asynchronous execution of time-consuming tasks, we rewrite the handleMessage() method of Handler’s Callback interface. It is also possible to use the POST () method instead of overriding the method for time-consuming task operations.

Handler workderHandler = new Handler(handlerThread.getLooper()); workderHandler.post(newRunnable() {    @Override    public void run() {// run in a worker thread (child thread) to implement its own message processing}});Copy the code

Finally, we can send time-consuming tasks to the worker HandlerThread in the form of a message by calling workerHandler, which is actually executed in handleMessage() in handler.callback.

Note that the start() method must be called before the Handler is created as the message executor of the HandlerThread, because the Looper argument needed to create the Handler is obtained from the HandlerThread. The assignment to the Looper object is created in the Run () method of the HandlerThread.

Example of HandlerThread

import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * Created by Administrator on 2016/9/18. */public class HandlerThreadActivity extends Activity implements Handler.Callback { private DBHandlerThread mDBHandlerThread; private Handler mUIHandler; // Handler Button mBtnQuery associated with the UI thread; TextView mTextResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);        mBtnQuery = (Button) findViewById(R.id.buttonQuery);        mTextResult = (TextView) findViewById(R.id.result);        mBtnQuery.setOnClickListener(new View.OnClickListener() {@override public void onClick(View v) {Message MSG = message.obtain (null, DBHandlerThread.MSG_QUERY_FRIENDS); //mDBHandlerThread.getWorkerHandler().sendMessage(msg); mDBHandlerThread.queryFriends(); }}); mUIHandler = new Handler(this); initWorkerThread(); } protected voidinitWorkerThread() {        mDBHandlerThread = new DBHandlerThread("Handler Thread"); mDBHandlerThread.setUIHandlerCallBack(mUIHandler); mDBHandlerThread.start(); // Thread run() is executed} @override protected voidonDestroy() {        super.onDestroy();        mDBHandlerThread.setUIHandlerCallBack(null);        mDBHandlerThread.quit();        mDBHandlerThread = null;    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            case DBHandlerThread.MSG_QUERY_FRIENDS:                // update UI                break;        }        return false;    }}Copy the code

The DBHandlerThread class looks like this:

import android.os.Handler; import android.os.HandlerThread; import android.os.Message; /** * Created by Administrator on 2016/9/18. */public class DBHandlerThread extends HandlerThread implements Handler.Callback { public static final int MSG_QUERY_FRIENDS = 100; private Handler mWorkerHandler; // Handler associated with the worker thread private Handler mUIHandler; // Handler associated with the UI thread public DBHandlerThread(String name) {super(name); } public DBHandlerThread(String name, int priority) { super(name, priority); } public voidsetUIHandlerCallBack(Handler handler) {        this.mUIHandler = handler;    }    public Handler getWorkerHandler() {        return mWorkerHandler;    }    @Override    protected void onLooperPrepared() {        mWorkerHandler = new Handler(getLooper(), this);    }    @Override    public boolean handleMessage(Message msg) {        switch (msg.what) {            caseMSG_QUERY_FRIENDS: //... Querying database operations... Message message = Message.obtain(null, MSG_QUERY_FRIENDS); mUIHandler.sendMessage(message); // Notify UI updatesbreak;        }        return true;    }    public void queryFriends() {        Message msg = Message.obtain(null, MSG_QUERY_FRIENDS);        mWorkerHandler.sendMessage(msg);    }}Copy the code

HandlerThread source code parsing

The source code for HandlerThread is limited, so let’s take a look at its constructor:

/** * Handy class for starting a new thread that has a looper. The looper can thenbe * used to create handler classes. Note that start() must still be called. */public class HandlerThread extends Thread  { int mPriority; // Thread priority int mTid = -1; // The current thread id // the Looper object held by the current thread Looper mLooper; Public HandlerThread(String name) {// Call the default method of the parent class to create thread super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) {super(name); mPriority = priority; }... }Copy the code

As you can see from the code, HandlerThread takes two constructors that pass in two parameters: name, which refers to the name of the thread, and priority, which refers to the thread priority. The priority of a thread ranges from -20 to 19. Those with higher priorities get more CPU resources, and those with lower priorities get less. -20 indicates the highest priority and 19 indicates the lowest priority. The default priority of HandlerThread is process.thread_priority_default, which has a value of 0. This priority is set in the run() method. Let’s look at its run() method:

public class HandlerThread extends Thread {  /**     * Call back method that can be explicitly overridden if needed to execute some     * setup before Looper loops.     */    protected void onLooperPrepared() {    }    @Override    public void run() { mTid = Process.myTid(); // Get the id of the current thread looper.prepare (); Synchronized (this) {mLooper = Looper. MyLooper (); synchronized (this) {mLooper = Looper. // Wake up the wait thread notifyAll(); } / / set the current thread priority Process. The setThreadPriority (mPriority); // Do some work before the thread loops (subclasses may or may not) onLooperPrepared(); // start loop looper.loop (); mTid = -1; }}Copy the code

The run() method constructs a loop thread with looper.prepare () and looper.loop (). Note here that the start() method of the HandlerThread object must be called after it is created to execute the run() method body.

After looper.prepare () is executed, the Looper object is created. The Looper object is then assigned to the internal variable mLooper of the HandlerThread using a synchronous lock mechanism. NotifyAll () is used to wake up the waiting thread. We then give priority to the thread and execute the onLooperPrepared() method, which is an empty implementation left to override if necessary to do some initialization. Finally, start the message queue in the thread by executing looper.loop ().

We see the wake-wait thread in the run() method. Why do we do this? The answer is in the getLooper() method:

/** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */public Looper getLooper() {   if(! IsAlive ()) {// Check whether the current thread is startedreturn null;   }   // If the thread has been started, wait until the looper has been created.   synchronized (this) {       while (isAlive() && mLooper == null) {           try {               wait(a); Wait until another thread calls notify() or notifyAll() to wake it up} catch (InterruptedException e) {}}}returnmLooper; }Copy the code

This method is used to get the Looper object instance associated with the current child thread HandlerThread. Check whether the HandlerThread is alive. If not, return null. If not, proceed to the block and check whether the Looper object is empty and the thread is started. The Looper object is not returned until it has been successfully created and the wait thread is woken up with notifyAll().

The Looper object is created in the run() method, which is executed in the child thread, while the getLooper() method is called in the UI thread. Without the wake-up mechanism, there is no guarantee that the Looper object will be created when the getLooper() method is called in the UI thread. There is a synchronization problem, so HandlerThread uses a wake-up mechanism to solve this synchronization problem.

HandlerThread is a loop thread, so how to exit? There are two ways to quit() and quitSafely() :

/** * Quits the handler thread's looper. * <p> * Causes the handler thread's looper to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * 

* Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. *

* * @return True if the looper has been asked to quit or false if the * thread had not yet started running. * * @see #quitSafely */public boolean quit() { Looper looper = getLooper(); if (looper ! = null) { looper.quit(); return true; } return false; }
Copy the code
/** * Quits the handler thread's looper safely. * <p> * Causes the handler thread's looper to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * Pending delayed messages with due times in the future will not be delivered. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * 

* If the thread has not been started or has finished (that is if * {@link #getLooper} returns null), then false is returned. * Otherwise the looper is asked to quit and true is returned. *

* * @return True if the looper has been asked to quit or false if the * thread had not yet started running. */public boolean quitSafely() { Looper looper = getLooper(); if (looper ! = null) { looper.quitSafely(); return true; } return false; }
Copy the code

A look at the developer documentation shows that the quit() method was added at API Level 5 and the quitSafely() method was only added at API Level 18.

The quit() method empties MessageQueue of all messages, both delayed and non-delayed.

The quitSafely() method simply emptying the MessageQueue of all delayed messages, distributing all non-delayed messages and waiting until the Handler is finished before stopping the Looper loop.

A delayed message here refers to a message sent via methods such as sendMessageDelayed or postDelayed.

5. Application scenarios of HandlerThread

HandlerThread is suitable for single-thread and asynchronous queue scenarios, such as I/O operations on databases and files. It does not take much time and does not cause large blocking. HandlerThread is not suitable for network IO operations because it has only one thread and has to queue up one by one.

Here are a few more examples of the single-thread + asynchronous queue model:



  1. The data initialization operation when the application is just started, if multiple threads are started to execute at the same time, it may compete for THE CPU execution time of UI threads, resulting in the lag. Using this model, through setting the priority, we can synchronize the execution of the work order without affecting the UI initialization.
  • 2. Read data from the database and display it in the ListView. The postAtFrontOfQueue method of the Handler quickly adds the read operation to the front of the queue for execution, and returns it to the main thread to update the UI if necessary.

  • 3.HandlerThread applies to IntentService. IntentService is a Service that executes tasks for a long time and has a higher priority than threads.

  • The last

    Have the friend that needs more information can private message me [advanced] I can share to you.

    Click directly to collect

    Android learning PDF+ architecture video + interview document + source notes

    If you need anything else, you can also check it out on GitHub. The following information will be uploaded to GitHub as well

    330 pages PDFAndroid core notes