Source Code Analysis of Android HandlerThread

Keywords: Java Android SDK Google

Handler Thread Introduction:

We know that Thread threads are disposable consumer goods. When Thread threads complete a time-consuming task, the threads are automatically destroyed. If I had another one at this time

A time-consuming task needs to be performed, and we have to recreate the thread to perform the time-consuming task. However, there is a performance problem: it is expensive to create and destroy threads many times

System resources. To understand this problem, we can build a looping thread, Looper Thread, which consumes thread execution when time-consuming tasks are put into the looping thread.

A time-consuming task, after which the loop thread is in a waiting state until the next time-consuming task is put in. This avoids creating Thread threads many times.

Performance issues. Maybe you can build a circular thread by yourself, but I can tell you the good news that there is actually a framework for circular threads in the Aandroid SDK.

Now. At this point you just need to know how to use it ok ay! Of course, our protagonist today, Handler Thread! Next, let Handler Thread come on and applaud.

HandlerThread's parent class is Thread, so HandlerThread is actually a thread, but it helps you implement a Looper loop internally. Then we

Let's see how Handler works first.

[Reproduced from: Source Code Analysis of Android HandlerThread Trees in Ruins

HandlerThread uses steps:

1. Create instance objects

HandlerThread handlerThread = new HandlerThread("handlerThread");

The above parameters can be arbitrary strings. The main function of the parameters is to mark the name of the current thread.

2. Start the HandlerThread thread

handlerThread.start();

At this point, we have built a loop thread. So you might wonder, how do I put a time-consuming asynchronous task into the Handler Thread thread to execute? Of course, there's a way. Let's look at the third one.

3. Constructing a cyclic message processing mechanism

Handler subHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //Implementing your own message processing
                return true;
            }
        });

The third step is to create a Handler object, which takes the looper object in HandlerThread above as the most Handler parameter, and then overrides the Callback interface class of Handler.

The handlerMessage method handles time-consuming tasks.

Summary: The order of the above three steps should not be disorderly, and they must be strictly followed. At this point, we can call subHandler to send time-consuming tasks to threads as messages

To execute in Handler Thread. The implication is that the handlerMessage method in the Callback interface class in subHandler is actually executed in the worker thread.

HandlerThread example:

package com.example.handlerthread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

    private Handler mSubHandler;
    private TextView textView;
    private Button button;

    private Handler.Callback mSubCallback = new Handler.Callback() {
        //The implementation of this interface deals with asynchronous time-consuming tasks, so the method is executed in sub-threads.
        @Override
        public boolean handleMessage(Message msg) {

            switch (msg.what) {
            case 0:
                Message msg1 = new Message();
                msg1.what = 0;
                msg1.obj = java.lang.System.currentTimeMillis();
                mUIHandler.sendMessage(msg1);
                break;

            default:
                break;
            }

            return false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textView);
        button = (Button) findViewById(R.id.button);

        HandlerThread workHandle = new HandlerThread("workHandleThread");
        workHandle.start();
        mSubHandler = new Handler(workHandle.getLooper(), mSubCallback);

        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                //Put asynchronous time-consuming tasks into Handler Thread
                mSubHandler.sendEmptyMessage(0);
            }
        });

    }
}

Handler Thread Source Analysis

HandlerThread Construction Method

/**
 * 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.
 */
public class HandlerThread extends Thread {
    //thread priority
    int mPriority;
    //Current thread id
    int mTid = -1;
    //Loper objects held by the current thread
    Looper mLooper;
    
    //Construction method
    public HandlerThread(String name) {
        //Calling the default method of the parent class to create a thread
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
   //Construction Method with Priority Parameters
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

...............

}
    

Analysis: This class begins with a description: this class is used to create a thread with Looper loop, Looper object is used to create Handler object, it is noteworthy to create Handler object.

You need to call the start() method to start the thread before the object. Some people may have questions here? Why do you need to call the start() method before you can create a Handler? We will answer later.

As the above code comment is clear, the HandlerThread class has two constructions, the difference being to set the priority parameters of the current thread. You can set priorities according to your situation.

The default priority can also be used.

run Method of Handler Thrad

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() {
        //Get the id of the current thread
        mTid = Process.myTid();
        //Preparing cycle conditions
        Looper.prepare();
        //Holding the lock mechanism to get the Loper object of the current thread
        synchronized (this) {
            mLooper = Looper.myLooper();
            //Notify that the current thread has successfully created the mLooper object, mainly notifying wait in the getLooper method
            notifyAll();
        }
        //Setting the priority of the current thread
        Process.setThreadPriority(mPriority);
        //The implementation volume of this method is empty, and subclasses can implement this method. The purpose is to do some preparatory work before the thread loop, of course, subclasses can also not be implemented.
        onLooperPrepared();
        //Start loop
        Looper.loop();
        mTid = -1;
    }
}

Analysis: The annotations in the above code are clearly written. The main function of the run method is to call Looper.prepare and Looper.loop to construct a loop thread. It is worth mentioning.

Yes, the onLooperPrepared method is called in the run method before the loop loop is started. The implementation of this method is empty, and the user can implement this method in the subclass. The function of this method is to

Do some initialization work before the thread loop, of course, you can also not implement this method, depending on the requirements. As you can see, Google engineers also consider the extensibility of the code when they write it. Cow B!

Other methods of HandlerThread

Get Looper gets the Loper object of the current thread

 /**
     * 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 the thread is not alive, it returns null directly
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        //If the thread has started, but Looper has not yet been created, wait until Looper is created successfully.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

Analysis: In fact, the English annotations at the beginning of the method have been explained clearly: the main function of this method is to obtain the mLooper object in the current HandlerThread thread.

The first step is to determine whether the current thread is alive or not, which returns null directly. Second, if the current thread survives, the member variable mLooper of the thread is determined to be null, if it is

null, indicating that the current thread has been successfully created, but it is not yet time to create the Looper object, so the wait method is called here to wait, after the notifyAll method in the run method is called.

Notify the current thread's wait method to wait for the end, jump out of the loop, and get the value of the mLooper object.

Summary: There is a synchronization problem in obtaining mLooper objects. Only when threads are successfully created and Looper objects are successfully created can mLooper values be obtained. Here we wait for the method wait and the notifyAll method in the run method to complete the synchronization problem together.

quit ends the current thread's loop

 /**
     * 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.
     * </p><p class="note">
     * 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.
     * </p>
     *
     * @return True if the looper 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;
    }
//Secure Exit Cycle
 public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

Analysis: There are two ways to get the current thread out of the loop, one is safe, the other is unsafe. As for the difference between the two methods, quitSafely method is a little less efficient than quit method, but safe. The specific choice depends on the specific project.

Conclusion:

1.HandlerThread is suitable for constructing circular threads.

2. The start method must be invoked when creating Handler as the message executor of HandlerThread thread, because the Looper parameters needed to create Handler are obtained from the HandlerThread class, and the assignment of Looper objects is created in the run method of HandlerThread.

3. For the use of HandlerThread and Service, please refer to another blog: Source Code Analysis of Android IntentService

[Reproduced from: Source Code Analysis of Android HandlerThread Trees in Ruins

Sweep code pays attention to the public number "Android knowledge dissemination" of Wechat, and periodically disseminates the commonly used Android basic knowledge.

Posted by alpine on Sat, 18 May 2019 10:40:13 -0700