Introduction to HandlerThread

In The Android system, we need to start another child thread to execute the time-consuming operation, and the thread will be destroyed automatically after the execution. Imagine if we were in a project where we were constantly doing time-consuming operations, and if we were constantly starting threads and then destroying threads, that would be very costly, right? So what’s the solution?

  • Using thread pools
  • useHandlerThread

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.

A handy class for creating a thread with Looper. This Looper can be used to create a Handler. Note that the start() method must be called first.

1. Application scenario of HandlerThread

HandlerThread is encapsulated by Google for us, which can be used to perform multiple time-consuming operations without the need to open the thread many times. It is implemented by using Handler and Looper.

2.HandlerThread is easy to use

MHandlerThread = new HandlerThread("main"); // getLooper in HandlerThead Looper Looper = mhandlerthread.getlooper (); Handler = new Handler(Looper); // Perform the time-consuming operation handler.post(new)Runnable() {
    @Override
    public void run() {// execute time-consuming operation in child thread}}); // Looper@override protected void is destroyed when the interface is destroyedonDestroy() {
    super.onDestroy();
    mHandlerThread.quit();
}
Copy the code

HandlerThread source code analysis

1. Member variables

int mPriority; // Thread priority int mTid = -1; // thread ID Looper mLooper; // Create thread LooperCopy the code

2. Construction method

public HandlerThread(String name) { super(name); mPriority = Process.THREAD_PRIORITY_DEFAULT; } public HandlerThread(String name, int priority) { super(name); mPriority = priority; // Thread priority}Copy the code

There are two constructors, one method and two methods, where name is the name of the thread and priority is the priority of the thread.

3. Start the thread to callstart()Method, in fact, is executedrun()methods

public void run() { mTid = Process.myTid(); Looper.prepare(); Synchronized (this) {mLooper = Looper. MyLooper (); synchronized (this) {mLooper = Looper. // Notify the current thread that the mLooper object has been successfully createdwaitnotifyAll(); } / / set the thread priority level Process. The setThreadPriority (mPriority); OnLooperPrepared (); onLooperPrepared(); onLooperPrepared(); Looper.loop(); mTid = -1; }Copy the code

4. So why is there a locking mechanism andnotifyAll()The method, the reason isgetLooper()methods

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

There is a synchronization problem in getting the mLooper object, and the value of the mLooper cannot be obtained until the thread is started and the Looper object is successfully created.

5. Take a lookquit()andquitSafe()Two methods

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

The difference in displacement between the two methods is looper.quit() and looper.quitsafely (), which are tracked separately

public void quit() {
    mQueue.quit(false);
}
public void quitSafely() {
    mQueue.quit(true);
}
Copy the code

The mqueue.quit () method passes false and true

void quit(boolean safe) {
    if(! mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if(safe) {/ / safety exit calls this method removeAllFutureMessagesLocked (); }else{// Unsafe exit executes this method removeAllMessagesLocked(); } // We can assume mPtr ! = 0 because mQuitting was previously false. nativeWake(mPtr); }}Copy the code

It is not safe to call removeAllMessagesLocked(), which essentially iterates through the list of messages, removing all callbacks to the messages, and resetting them to NULL.

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while(p ! = null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }Copy the code

Safely invoked removeAllFutureMessagesLocked () this method, it will be according to the Message. When this property, whether our current Message queue is processing the Message, no news is processing, remove all callbacks directly, is processing, Wait for the message processing to complete before exiting the loop. So quitSafe() is safe, and the quit() method is not, because the quit() method simply removes all callbacks regardless of whether the message is being processed.

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if(p ! = null) {// Determine if the message in the current queue is being processed, if not, remove all callbacksif (p.when > now) {
            removeAllMessagesLocked();
        } else{// If the Message is being processed, wait for the Message to finish processing before exiting the loop Message n;for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while(n ! = null); }}}Copy the code

Third, summary

  • If you have to start a thread all the time, and then you have to destroy a thread, it can be very costly,HandlerThreadA good solution to this problem;
  • HandlerThreadAs asynchronous operations are placedHandler, so it is serial, but only suitable for time-consuming operations with less concurrency.