English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

In-depth Explanation of the Relationship Between Thread, Handler, and HandlerThread

Preface

A few days ago, I saw an interview question: What are the differences between Thread, Handler, and HandlerThread? This question is quite interesting. For many people, Thread and Handler may be familiar, mainly involving Android's message mechanism (Handler, Message, Looper, MessageQueue), see

But what is this HandlerThread used for? Is it a Handler or a Thread? We know that Handler is used for asynchronous UI updates, more specifically, it is used for inter-thread communication. When updating UI, it is the communication between the sub-thread and the UI main thread. So now if we want to communicate between sub-threads, how should we do it? In the end, it is also with Handler.+Thread to complete (not recommended, need to operate Looper manually), Google has very considerately wrapped a class for us, that is the one mentioned earlier: HandlerThread. (Similar wrapping for multi-threading scenarios includes AsyncTask)

Usage method

Let's take a look at the usage method of HandlerThread first:
First, create a HandlerThread and execute start()

private HandlerThread mHandlerThread;
......
mHandlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

Create a Handler using mHandlerThread.getLooper() to generate Looper:

 final Handler handler = new Handler(mHandlerThread.getLooper()){
   @Override
   public void handleMessage(Message msg) {
    System.out.println("Received message");
   }
  });

Then create a new sub-thread to send messages:

 new Thread(new Runnable() {
   @Override
   public void run() {
    try {
     Thread.sleep(1000);//Simulate time-consuming operation
     handler.sendEmptyMessage(0);
    }
     e.printStackTrace();
    }
   }
  }).start();

Finally, do not forget to release in onDestroy to avoid memory leaks:

 @Override
 protected void onDestroy() {
  super.onDestroy();
  mHandlerThread.quit();
 }

The execution result is very simple, it is to print the string: Received message on the console

Principle

The entire usage process does not require us to care about the Handler-related things, we just need to send messages, process messages, and let the Looper-related things be handled by itself. Let's take a look at the source code to see how it is implemented, let's start with the constructor:

public class HandlerThread extends Thread {}

What's the difference between HandlerThread and a regular thread?

public class HandlerThread extends Thread {
 int mPriority;
 int mTid = -1;
 Looper mLooper;
 public HandlerThread(String name) {
  super(name);
  mPriority = Process.THREAD_PRIORITY_DEFAULT;
 }
 ......
}

The answer is that there is an additional Looper, which is unique to the child thread, used for fetching and processing messages. Let's continue to look at the run method of the HandlerThread thread: 

 protected void onLooperPrepared() {
 }
 @Override
 public void run() {
  mTid = Process.myTid();
  Looper.prepare();
  synchronized (this) {
   mLooper = Looper.myLooper();//Generate Looper
   notifyAll();
  }
  Process.setThreadPriority(mPriority);
  onLooperPrepared();//Empty method, called after the Looper is created, can overwrite the logic by yourself
  Looper.loop();//Dead loop, continuously taking messages from the MessageQueue and handing them over to the Handler for processing
  mTid = -1;
 }

It mainly performs some Looper operations, if we use Handler ourselves+If implemented by Thread, this operation also needs to be performed, let's take a look at the getLooper() method:   

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();
    }
    }
   }
  }
  return mLooper;
 }

The method is very simple, just adding a synchronization lock. If it has been created (isAlive() returns true) but mLooper is empty, it will continue to wait until mLooper is created successfully. Finally, let's take a look at the quit method, and it is worth mentioning that there are two:

 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;
 }

quitSafely is for the situation where there are still messages in the message queue or delayed messages that have not been processed. After calling this method, they will all be stopped.

Summary

The usage method of HandlerThread is relatively simple, but we need to understand one thing: if a thread needs to process messages, it must have its own Looper, not where the Handler is created, can handle messages.

If you don't use HandlerThread, you need to manually call methods such as Looper.prepare() and Looper.loop().

This is the material organization of the relationship between Thread, Handler, and HandlerThread. We will continue to supplement relevant materials in the future, thank you all for your support of this website!

Statement: The content of this article is from the Internet, the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume relevant legal liability. If you find any content suspected of copyright infringement, please send an email to notice#w3Please report abuse by sending an email to codebox.com (replace # with @ in the email address) and provide relevant evidence. Once verified, the website will immediately delete the content suspected of infringement.

You May Also Like