An overview of the

HandlerThread is a Thread with a Handler message loop mechanism, which can be said to be the combination of Handler + Thread, from the source code is also so designed.

Android Handler source code analysis (detailed), will teach you step by step to understand the Handler.

In general, if a child Thread needs to interact with the main Thread, it can be designed with HandlerThread. This is more convenient than a simple Thread, and it is easier to manage, because we all know that the life cycle of a Thread is uncontrollable in some cases. For example, the direct new Thread().start() method is not recommended in the project, in fact, there are many places in the Android source code use HandlerThread, I will analyze HandlerThread usage and source code parsing.

Use the sample

HandlerThread HandlerThread = new HandlerThread(" HandlerThread "); // Start the thread handlerthread.start (); Handler = new Handler(handlerThread.getLooper()) {@override public void Override handleMessage(Message msg) { super.handleMessage(msg); }};Copy the code

Note: the order of use cannot be changed!! The handler parameter getLooper that creates the main thread is not available unless the child thread is started.

The Demo,

This simulates downloading something from a child thread and then communicating with the main thread. The main thread knows when the download starts and ends, so it can change the UI in time.

The first is the DownloadThread class, which inherits from HandlerThread and is used for downloading.

public class DownloadThread extends HandlerThread{ private static final String TAG = "DownloadThread"; public static final int TYPE_START = 2; Public static final int TYPE_FINISHED = 3; Private Handler mUIHandler; // Handler public DownloadThread(String name) {super(name); } /* Override protected void onLooperPrepared() {log. e(TAG, "onLooperPrepared: 1. super.onLooperPrepared(); Public void setUIHandler(Handler UIhandler) {mUIHandler = UIhandler; Log.e(TAG, "setUIHandler: 2. } public void startDownload() {log.e (TAG, "startDownload: 3. Notify the main thread when the Download thread starts downloading "); mUIHandler.sendEmptyMessage(TYPE_START); // Download log. e(TAG, "startDownload: 5. ); SystemClock.sleep(2000); Log.e(TAG, "startDownload: 6. mUIHandler.sendEmptyMessage(TYPE_FINISHED); }}Copy the code

Then there is the MainActivity section, the UI and processing messages.

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private DownloadThread mHandlerThread; // Private Handler mUIhandler; Override protected void onCreate(Bundle savedInstanceState) {super.oncreate (savedInstanceState); setContentView(R.layout.activity_main); MHandlerThread = new DownloadThread("mHandlerThread"); // Call the start method to start the thread mhandlerThread.start (); // Initialize Handler, MUIhandler = new Handler(mHandlerThread.getLooper()) {@override public void getLooper() HandleMessage (Message MSG) {// Identify the MSG from the mHandlerThread, Switch (msg.what) {case DownloadThread.TYPE_START: // Not here to change the UI, but at this time you can change the UI. Log.e(TAG, "4. You can change the main UI"); break; case DownloadThread.TYPE_FINISHED: Log.e(TAG, "7. The main thread knows that the Download thread is finished downloading... Change the main UI and call it a day "); break; default: break; } super.handleMessage(msg); }}; MUIhandler / / the child thread into the main thread, can be on duty in the child thread, at any time send a message back to the main thread. MHandlerThread setUIHandler (mUIhandler); . / / the child thread start download mHandlerThread startDownload (); } @override protected void onDestroy() {// There are two ways to exit mhandlerThread.quit (); //mHandlerThread.quitSafely(); Need API > = 18 super. OnDestroy (); }}Copy the code

The following logs are displayed

 

The source code parsing

public class HandlerThread extends Thread {
Copy the code

A HandlerThread is essentially a thread that holds a handler, so messages can be processed and distributed in child threads.

Let’s look at constructors:

int mPriority; Int mTid = -1; Looper mLooper; // Looper private @nullable Handler mHandler; public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } /** * Constructs a HandlerThread. * @param name * @param priority The priority to run the thread at. The value supplied must be from * {@link android.os.Process} and not from java.lang.Thread. */ public HandlerThread(String name, int priority) { super(name); mPriority = priority; }Copy the code

There are two constructors: HandlerThread(String name) and HandlerThread(String name, int priority). We can set the name and priority of the thread. Attention! Is the priority of Process, not 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();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
Copy the code

There is a method onLooperPrepared() in there, and in practice we can override this method to do some initialization. Run () is the key.

Looper. Prepare creates a Looper, and looper. myLooper retripes the current thread’s Looper.

The wait is called in the getLooper method of the HandlerThread to get the Looper of the HandlerThread. The wait is called in the getLooper method of the HandlerThread.

If you call getLooper without calling HandlerThread’s start method, wait until the run method runs and then wake up. After waking up, the run method continues to set the priority passed in the constructor, and then calls onLooperPrepared, which is an empty implementation so that it can be overwritten if something needs to be done before Looper starts polling.

Finally, call looper. loop to start polling. On exit, mTid = -1;

public Looper getLooper() { if (! isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }Copy the code

This method is used to fetch the current Looper. If the Looper is not fetched, it will wait until the Looper is fetched. If the Looper is fetched, all threads will wake up.

public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
Copy the code

This is the Handler that gets the Looper thread bound to HandlerThread

public boolean quit() { Looper looper = getLooper(); if (looper ! = null) { looper.quit(); return true; } return false; } public boolean quitSafely() { Looper looper = getLooper(); if (looper ! = null) { looper.quitSafely(); return true; } return false; }Copy the code

MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue

void quit(boolean safe) { if (! mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr ! = 0 because mQuitting was previously false. nativeWake(mPtr); }}Copy the code

You can see: When we call quit, we actually execute removeAllMessagesLocked from MessageQueue, which empties the MessageQueue message pool. Either a delayed message (a delayed message is a message sent via methods such as sendMessageDelayed or postDelayed, or a message that is not immediately executed is a delayed message) or a non-delayed message.

And quitSafely method, actually perform the MessageQueue removeAllFutureMessagesLocked method, through the name can be seen that this method can only empty MessageQueue messages all delays in the pool, Send out all the non-delayed messages in the message pool and let the Handler handle them. QuitSafely compared to the quit method is that it sends all the non-delayed messages before emptying the message; in a word, it eliminates future messages that need to be executed.

A common feature of both methods is that Looper no longer receives new messages, the message loop ends, and messages sent through Handler are no longer queued because the message queue has exited. One thing to note when using these two methods is that the quit method dates back to API 1, and quitSafely wasn’t added until API 18.

conclusion

  • If you have to start a thread all the time, and then you have to destroy a thread, it can be very costly,HandlerThread A good solution to this problem;
  • HandlerThread As asynchronous operations are placedHandler , so it is serial, but only suitable for time-consuming operations with less concurrency.
  • HandlerThread Call the exit method when you’re done.
  • Use handlers to avoid memory leaks