HandlerThread has these characteristics:

HandlerThread is essentially a Thread class that inherits threads; HandlerThread has its own internal Looper object for Looper loops; Asynchronous tasks can be performed in the handleMessage method by passing the Looper object of the HandlerThread to the Handler object. To create a HandlerThread, the handlerThread.start () method must be called. The Thread will first call the run method to create a Looper object. After understanding some of the features of the above HandlerThread, we will first take a look at the steps to use HandlerThread. 1. Create instance objects

1.  HandlerThread handlerThread = new HandlerThread("downloadImage");
Copy the code

The main function of the passed argument is to mark the name of the current thread, which can be any string.

2. Start the HandlerThread

Handlerthread.start (); handlerThread.start();Copy the code

At this point, we have created the HandlerThread and started the thread. So how do we put a time-consuming asynchronous task into a HandlerThread? Let’s look at the following steps:

3. Build a circular message processing mechanism

/** * callback implements handler. callback {@override public Boolean HandleMessage (Message MSG) {// Make the corresponding network request in the child thread // notify the main thread to update UI muiHandler. sendMessage(msg1); return false; }}Copy the code

4. Build an asynchronous handler

ChildHandler = new Handler(handlerThread.getLooper(),new ChildCallback()); Steps 1, 2, 3, and 4 build a handler that can be used for asynchronous operations and pass the Looper object and Callback interface class of the previously created HandlerThread as arguments to the current handler. The current asynchronous handler now owns the Looper object of the HandlerThread. Since the HandlerThread itself is an asynchronous thread, the Looper is bound to the asynchronous thread, so that the handlerMessage method can handle time-consuming tasks asynchronously. With our Looper+Handler+MessageQueue+Thread asynchronous loop built, let’s look at a complete use case.

The main code of the HandlerThread use case is as follows:

activity_handler_thread.xml

<ImageView
    android:id="@+id/image"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Copy the code
HandlerThreadActivity.java package com.zejian.handlerlooper; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.widget.ImageView; import com.zejian.handlerlooper.model.ImageModel; import com.zejian.handlerlooper.util.LogUtils; import java.io.BufferedInputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; /** * Created by zejian on 16/9/2. */ public Class HandlerThreadActivity extends Activity {/** * image address collection */ private String url[]={ "https://img-blog.csdn.net/20160903083245762", "https://img-blog.csdn.net/20160903083252184", "https://img-blog.csdn.net/20160903083257871", "https://img-blog.csdn.net/20160903083257871", "https://img-blog.csdn.net/20160903083311972", "https://img-blog.csdn.net/20160903083319668", "https://img-blog.csdn.net/20160903083326871" }; private ImageView imageView; private Handler mUIHandler = new Handler(){ @Override public void handleMessage(Message msg) { LogUtils. E (" times: "+ MSG. What); ImageModel model = (ImageModel) msg.obj; imageView.setImageBitmap(model.bitmap); }}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); imageView= (ImageView) findViewById(R.id.image); // Create asynchronous HandlerThread HandlerThread HandlerThread = new HandlerThread("downloadImage"); Handlerthread.start (); ChildHandler = new Handler(handlerThread.getLooper(),new ChildCallback()); for(int i=0; i<7; I++) {/ / every 1 second to update picture childHandler. The sendEmptyMessageDelayed (1000 * I, I); }} /** * callback implements handler. callback {@override public Boolean HandleMessage (Message MSG) {// Make network request in child thread Bitmap Bitmap =downloadUrlBitmap(url[MSG. ImageModel imageModel=new ImageModel(); imageModel.bitmap=bitmap; imageModel.url=url[msg.what]; Message msg1 = new Message(); msg1.what = msg.what; msg1.obj =imageModel; // Notify the main thread to update UI muiHandler. sendMessage(msg1); return false; } } private Bitmap downloadUrlBitmap(String urlString) { HttpURLConnection urlConnection = null; BufferedInputStream in = null; Bitmap bitmap=null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); bitmap=BitmapFactory.decodeStream(in); } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection ! = null) { urlConnection.disconnect(); } try { if (in ! = null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return bitmap; }}Copy the code

In this case, we created two handlers, a mUIHandler for updating the UI thread and a childHandler for downloading images asynchronously. The result is that childHandler notifies ChildCallback’s callback function handleMessage every second to download the image and tell mUIHandler to update the UI, using the sendEmptyMessageDelayed method. In fact, the most typical application on Android is IntentService. We will analyze this in the next part, but we will not go into details here. The screenshot of the case operation is as follows:

HandlerThread source code parsing is not much more than 140 lines, so step by step to analyze it, first to look at its constructor

/** * 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 { int mPriority; // Thread priority int mTid = -1; Looper mLooper; 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; } /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { }Copy the code

The HandlerThread is passed from Thread to Thread. The constructor passes two parameters, one is the name of the Thread, and the other is the priority of the Thread. The member variable mLooper is the Looper object held by the HandlerThread itself. OnLooperPrepared () is an empty implementation that can be overwritten if necessary, but before the Looper loop starts, look at the run method:

@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); / / wake up waiting thread} Process. SetThreadPriority (mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }Copy the code

If you create a HandlerThread object, you must call its start() method to perform other operations. If you call the start() method, you will start the thread. The Looper object will be created. When the Looper object is created, it will be bound to the current thread (i.e. the current asynchronous thread) so that we can assign the Looper object to the Handler. This ensures that the handleMessage method in the Handler object is executed on an asynchronous thread. The code will then execute:

synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); // Wake up the waiting thread}Copy the code

Looper object is assigned to the internal variable mLooper of HandlerThread, and notifyAll() is used to wake up the waiting thread. Looper.loop() is executed. Code to open the looper loop. So why wake up the wait thread here? Let’s look at the getLooper method

Public Looper getLooper() {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

If the thread is already started, it will enter the synchronization statement and determine whether the looper is null. Null means that the looper object has not been assigned, that is, it has not been created. At this point, the current calling thread enters the wait phase and does not return the Looper object until the Looper object is created and notifyAll() is used to wake up the waiting thread. The wait-up mechanism is needed because Looper creation is performed in the child thread. The getLooper method is called on the main thread, so there is no guarantee that the Looper has been created when we call getLooper, so we can see that there is a synchronization problem when we get the mLooper object. The value of mLooper is not available until the thread is successfully created and the Looper object is successfully created. The synchronization problem is solved by a wake-up mechanism inside the 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

As you can see from the source code, when we call quit, we are actually calling Looper’s quit method internally and we are actually executing removeAllMessagesLocked from MessageQueue. This method is mainly to empty all messages in the MessageQueue message pool, whether it is delayed messages (delayed messages are sent by sendMessageDelayed or postDelayed messages, etc.) or non-delayed messages. When call quitSafely method, its internal call is stars quitSafely method and the final execution is the removeAllFutureMessagesLocked MessageQueue method, This method only empties all the delayed messages in the MessageQueue message pool and sends all the non-delayed messages out to the Handler before stopping the Looper loop. The quitSafely method is safer than the quit method because it sends all the non-delayed messages before emptying the message pool. The last thing to note is that Looper’s quit method is based on API 1, while Looper’s quitSafely method is based on API 18. This completes all analysis of HandlerThread.

Related articles:

Android multithreading HandlerThread complete details

IntentService Android multithreaded IntentService

— — — — — — — —

Copyright notice: This article is originally published BY CSDN blogger “Zejian_”. It is subject to CC 4.0 BY-SA copyright agreement. Please attach the link of original source and this statement.

The original link: blog.csdn.net/javazejian/…

From zejian’s blog