• preface

If we want to perform multiple time-consuming tasks, the first thing to think about is to create multiple threads to perform the tasks. If we have learned about thread pools, we can also use thread pools. Is there anything lighter than thread pools? Handler to understand ~

What! Can Handler also perform time-consuming tasks? So here’s the problem

  • How does a child thread use Handler?

Let’s see if it works

new Thread(new Runnable() {
    @Override
    public void run(a) {
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("lkx", String.valueOf(msg)); }}; Message message = Message.obtain(); message.obj ="Hello"; handler.sendMessage(message); } }).start(); Execution result: E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.example.hello, PID: 12612
    java.lang.RuntimeException: CanT create handler inside thread thread [thread-2,5,main] that has not called looper.prepare () at android.os.Handler.
      
       (Handler.java:227) at android.os.Handler.
       
        (Handler.java:129) at com.example.hello.HandlerThreadActivity$2$1.
        
         (HandlerThreadActivity.java:42) at com.example.hello.HandlerThreadActivity$2.run(HandlerThreadActivity.java:42) at java.lang.Thread.run(Thread.java:923)
        Copy the code

Sure enough… Error message, there is a particularly familiar method in the error message looper.prepare ()

(ActivityThread.main() initiallooper (activityThread.main ()); (ActivityThread.main()); (ActivityThread.main(); Can we initialize it ourselves in the child thread?

new Thread(new Runnable() {
    @Override
    public void run(a) {
        Looper.prepare(); // Initialize the current thread Looper object
        Looper looper = Looper.myLooper(); // Get the Looper object for the current thread
        Handler handler = new Handler(looper) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("lkx", (String) msg.obj); }}; Message message = Message.obtain(); message.obj ="Hello";
        handler.sendMessage(message);
        Looper.loop(); // Note: Looper.loop is an infinite loop inside, which will block and cause subsequent code to fail to execute} }).start(); Result: Hello Thread-2
Copy the code

In this code, we see that the Handler can be used in the child thread, and the handlerMessage will be called back in the child thread, so we can do time-consuming operations in the handlerMessage.

It’s too much trouble to write it this way every time. Is there an easier way? B: of course! Android already gives us a class, HandlerThread

  • How to use HandlerThread?

// Create a HandlerThread and call the start() method
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a Handler and pass in the Looper object for HandlerThread
Handler handler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.d("lkx", (String) msg.obj); }};// Create 5 download tasks
for (int i = 0; i < 5; i++) {
    Message message = Message.obtain();
    message.obj = "Download Task"+ i; handler.sendMessage(message); } Execution result: Download task0Download task1Download task2Download task3Download task4
Copy the code
  • HandlerThread source parsing

At first glance, HandlerThread inherits Thread, which means that HandlerThread should be a Thread.

public class HandlerThread extends Thread {}
Copy the code
  • A constructor

Name: thread name priority: thread priority

public HandlerThread(String name) {
    super(name);
    mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

public HandlerThread(String name, int priority) {
    super(name);
    mPriority = priority;
}
Copy the code
  • HandlerThread#run

Looper.prepare() is initialized, then looper.loop () is called to open the loop. If looper.loop () is initialized, looper.loop () is called to open the loop

@Override
public void run(a) {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
Copy the code
  • HandlerThread#getLooper

GetLooper () is designed to ensure that the Looper object can be obtained normally. If the Looper object is null, it will call wait() to release the lock and enter the wait state while waiting for the run() method to initialize. When the run() method gets the Looper object, it calls notifyAll(), where wait() is awakened and the code continues until the Looper object is returned.

public Looper getLooper(a) {
    if(! isAlive()) {return null;
    }
    
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}
Copy the code
  • HandlerThread#quit

When not in use and we need to close the Looper loop, we can call handlerthread.quit () or handlerthread.quitsafely () to stop the loop. The difference between the two is that the handlerthread.quitsafely () is to stop the loop directly while the handlerthread.quitsafely () is to stop until the task is finished.

@Override
protected void onDestroy(a) {
    super.onDestroy();
    if(mHandlerThread! =null) { mHandlerThread.quit(); }}Copy the code
  • conclusion

MessageQueue () MessageQueue () MessageQueue () MessageQueue () MessageQueue () MessageQueue) The Handler will pass messages to the handleMessage via Looper’s loop. The loop will always be open, so we must close it when we are not using it.