HandlerThread parsing and application

This article is about HandlerThread, for those of you who don’t know or haven’t used this class before. The reason is simple: many native classes are obsolete in today’s frameworks, whereas early developers needed to create a Handler in Thread to update the UI in child threads.

class MyThread : Thread() {
    private lateinit var mHandler: Handler

    override fun run() {
        super.run()
        Looper.prepare()

        mHandler = object : Handler() {
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                if(msg? .what==0){ //... Handle messages}}} looper.loop ()}}Copy the code

Do you think it’s hard? Every time you want to process information in a child thread you have to create a Handler in that child thread, right?

However, a thoughtful Google engineer provided us with a class with a built-in Handler called HandlerThread.

Class notes

To learn the composition of a class, start with its class annotations.

**
 * 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 {
    ...
}
Copy the code
  • As can be seen from its comments, this is a file withLoopertheThreadtheLooperCan be used to createHandlerPlease note thatstart()This method still needs to be called.
  • To summarize, this class is a thread class with its own Handler.

A constructor

int mPriority; Int mTid = -1; Looper mLooper; // Looper private @nullable Handler mHandler; 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; }Copy the code
  • There are two constructors here, oneHandlerThread(String name), aHandlerThread(String name, int priority)We can set the name and priority of the thread. Attention! Is the priority of Process, not Thread.
Public static final int THREAD_PRIORITY_DEFAULT = 0; public static final int THREAD_PRIORITY_LOWEST = 19; public static final int THREAD_PRIORITY_BACKGROUND = 10; public static final int THREAD_PRIORITY_FOREGROUND = -2; public static final int THREAD_PRIORITY_DISPLAY = -4; public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; public static final int THREAD_PRIORITY_VIDEO = -10; public static final int THREAD_PRIORITY_AUDIO = -16; public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1; public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;Copy the code

Other methods

/* The method called before looper.loop () can be overridden if configuration is required */ protected voidonLooperPrepared() {} /* Run from Thread, called after start() */ @override public voidrun() { mTid = Process.myTid(); // Thread id looper.prepare (); Looper synchronized (this) {mLooper = looper.mylooper (); // Get Looper instance notifyAll(); / / native method, used to wakes up all threads that are waiting for which} Process. The setThreadPriority (mPriority); // Set thread priority onLooperPrepared(); // Call the above method looper.loop (); //Looper start polling mTid = -1; } /* Get Looper */ public LoopergetLooper() {// Check whether the thread is startedif(! isAlive()) {return null;
        }
        
        synchronized (this) {
            while(isAlive() &&mlooper == null) {// If the thread is started and Looper is null try {wait(a); // Make it wait until Looper} catch (InterruptedException e) {}}}returnmLooper; } /* Get Handler */ @nonnull public HandlergetThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }

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

    
    public int getThreadId() {
        return mTid;
    }
Copy the code
  • I wrote the comment in the code to use getLooper().quit() to exit the HandlerThread when it is not in use, but note the difference between quit and quitSafely, which I’ll discuss in a shorter section.

The difference between quit() and quitSafely()

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

Looper calls the quit() and quitSafely() methods in both methods. MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue

Looper:

quitSafely:

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

quit:

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

If you’re familiar with messaging, you probably know that MessageQueue is a queue used to process messages. If you’re not, check it out. Without further ado, let’s dive into MessageQueue to see what the Quit (Boolean Safe) method is.

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

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

            if(safe) {// Knock on the blackboard! This is the difference between quitSafely and quit removeAllFutureMessagesLocked (); }else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
Copy the code
  • When we areLooperIn the callquitSafely()When theMessageQueueIn the callremoveAllFutureMessagesLocked()methods
  • When we areLooperIn the callquit()When theMessageQueueIs called inremoveAllMessagesLocked()methods
  • The essential difference between the two methods is the scope of the remove message. Here is the pseudocode:
            if(safe) {empty only the queue of delayed messages (sent via postDelay) and continue sending non-delayed messages until complete}else{remove all messages, including delayed messages}Copy the code

So the difference between safe is whether or not all messages are cleared. We won’t go into the implementation of these two methods. After all, this article is about handlerThreads, not data structures.

application

  • IntentServiceThe bottom line is utilizationHandlerThread
  • Take an example of an Activity:
class MainActivity : AppCompatActivity() ,Handler.Callback{


    private lateinit var mUIHandler :Handler
    private lateinit var mDownloadThread: DownloadThread

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        init()
    }

    private fun init(){
        mUIHandler = Handler(this)

        val list = mutableListOf<String>()
        list.add("url1")
        list.add("url2")
        list.add("url3")

        mDownloadThread = DownloadThread("Download")
                        .setUrls(list)
                        .setUIHandler(mUIHandler)

        mDownloadThread.start()
        Log.d("test"."Start downloading") } override fun handleMessage(msg: Message?) : Boolean { when(msg? .what){ DownloadThread.START->{ Log.d("test"."The Activity receives the START message")
            }
            DownloadThread.FINISH->{
                Log.d("test"."The Activity received the FINISH message")}}return true}}Copy the code





DownloadThread:

class DownloadThread(name: String?) : HandlerThread(name), Handler.Callback {
    private lateinit var mWorkHandler: Handler
    private lateinit var mUIHandler: Handler
    private lateinit var urls: List<String>

    companion object {
        const val START = 1
        const val FINISH = 2
        const val KEY = "getUrl"
    }

    fun setUIHandler(mUIHandler: Handler): DownloadThread {
        this.mUIHandler = mUIHandler
        return this
    }

    fun setUrls(urls: List<String>): DownloadThread {
        this.urls = urls
        return this
    }

    override fun onLooperPrepared() {
        super.onLooperPrepared()
        if(looper ! = null) mWorkHandler = Handler(looper, This) / / here download information sent urls. ForEach {url - > val message = mWorkHandler. ObtainMessage () val bundle = bundle () Bundle.putstring (KEY, URL) message.data = bundle mworkHandler. sendMessage(message)}} // handleMessage of mWorkHandler override fun handleMessage(msg: Message?) : Boolean {if (msg == null || msg.data == null) return falseVal url = msg.data.get(KEY) as String Message = mUIHandler.obtainMessage(START) mUIHandler.sendMessage(startMessage) Log.d("test"."$url":Thread sends START message")

        Thread.sleep(2000)
        Log.d("test"."$url:Thread performing time-consuming operations...")

        val finishMessage: Message = mUIHandler.obtainMessage(FINISH)
        mUIHandler.sendMessage(finishMessage)
        Log.d("test"."$url:Thread sends FINISH message")
        return true}}Copy the code

Results:

The 2019-01-30 20:47:47. 743 D /test: Start download 2019-01-30 20:47:47.744 D/test: urL1 :Thread send START message 2019-01-30 20:47:47.776 D/test: The Activity receives the START messagetest: urL1 :Thread Performing time-consuming operations... The 2019-01-30 20:47:49. 747 D /testUrl1 :Thread sends FINISH message 2019-01-30 20:47:49.747 D/test: The Activity receives the FINISH messagetest: url2 :Thread send START message 2019-01-30 20:47:49.747 D/test: The Activity receives the START messagetest: url2 :Thread Performing time-consuming operations... The 2019-01-30 20:47:51. 749 D /testUrl2 :Thread sends FINISH message 2019-01-30 20:47:51.750 D/test: The Activity receives the FINISH messagetest: url3 :Thread send START message 2019-01-30 20:47:51.750 D/test: The Activity receives the START messagetest: url3 :Thread Performing time-consuming operations... The 2019-01-30 20:47:53. 751 D /testUrl3 :Thread sends FINISH message 2019-01-30 20:47:53.75d /testThe: Activity receives the FINISH messageCopy the code
conclusion

This is how HandlerThread performs serial tasks in child threads and feeds back to the main thread. That’s all you need to know about HandlerThread in general. You’re welcome to point out any errors or omissions.