Android advanced – Handler bottom source code analysis two

  • How many handlers are there in a thread?
  • How many loopers are there in a thread? How to guarantee?
  • Handler memory leak cause? Why didn’t any of the other inner classes say there was a problem?
  • Why can the main thread use new Handler? What does the new Handler have to do if I want to prepare it in a child thread?
  • What is the Looper maintained in the child thread when there is no message in the message queue? What’s the use?
  • Since there can be multiple handlers to add data to a MessageQueue (each Handler may be on a different thread when sending messages), how is it internally thread-safe? What about getting messages?
  • How should we create Message when we use it?
  • What does the data structure of Message look like?
  • Why does Looper loop death not cause apps to freeze
  • What is a HandlerThread?

Handler handler Handler Handler Handler Handler Handler Handler Handler

How many handlers are there in a thread?

A thread can have multiple handlers, one for each new thread

How many loopers are there in a thread? How to guarantee?

There can only be one Looper per thread,

Looper is initialized with ThreadLocal when looper.prepare () is called.

Handler.java static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() ! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }Copy the code

The sthreadlocal.get () method is used to check whether the current TheradLocal has a value. If so, Only one Looper may be created per is displayed Thread exception. If there is no Looper set by the set() method, only one Looper can exist in a thread

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
Copy the code

MessageQueue() is initialized when Looper is initialized, so there is only one MessageQueue() in a thread.

Handler memory leak cause? Why didn’t any of the other inner classes say there was a problem?

Handler memory leaks because the inner class holds references to the outer class



Here the Handler holds a reference to the Activity because the Handler can call the Activity’s methods

When handler.sendxxx /postXX is called to send a Message, the enqueueMessage() method is called. MSG. Target saves the Handler object, so Message holds the Handler object

class Message{ Handler target; } class Handler{ private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }}Copy the code

If you now have a Message waiting for 5 minutes to execute, but the page is closed and the Message is still in the queue waiting to execute, Message holds the Handler object, and the Handler holds the Activity object, so the Activity won’t be collected by GC and cause a memory leak.

Why don’t other internal classes leak memory, such as ViewHolder in RecyclerView?

Because the life cycle is different, the ViewHolder will disappear when the RecyclerView disappears.

Why can the main thread use new Handler? What does the new Handler have to do if I want to prepare it in a child thread?

Looper.prepare() and looper.loop () were called by ActivityThread at startup

A:

new Thread(() -> { Looper.prepare(); TextView tv = findViewById(R.id.tv); Tv.settext (" Qingdao Beer 111"); Looper.loop(); }).start();Copy the code

Rendering (1.1)

Method 2:

HandlerThread = new HandlerThread("TextHandler"); // Start the thread handlerthread.start (); Handler1 = new Handler(handlerThread.getLooper()) {handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); TextView tv2 = findViewById(R.id.tv2); Tv2.settext (" currentThread name: "+ thread.currentthread ().getname ()); }}; // Use thread Handler to send Message MSG = message.obtain (); msg.what = 2; Handler1. sendMessage(MSG);Copy the code

Rendering (1.2)

Reference from: www.jianshu.com/p/9c10beaa1…

What is the Looper maintained in the child thread when there is no message in the message queue? What’s the use?

Call the looper.quit () method

  • Looper.quit():

    • All attempts to put a Message into a Message queue will fail, such as handler.sendMessage (), which returns false but is unsafe. It is possible that a Message is still in the Message queue and terminates the Looper without being processed.
  • Looper.quitsafely()

    • The call terminates Looper after all messages have been processed, and all attempts to put a Message into a Message queue fail.

Reference from: www.jianshu.com/p/2c7c7863d…

Since there can be multiple handlers to add data to a MessageQueue (each Handler may be on a different thread when sending messages), how is it internally thread-safe? What about getting messages?

Class MessageQueue{// Add a Message to the queue Boolean enqueueMessage(Message MSG, long WHEN) {... synchronized (this) { ... If (needWake) {// Native underlying wake nativeWake(mPtr); } } return true; } // Message next() {... for (;;) NativePollOnce (PTR, nextPollTimeoutMillis); synchronized (this) { ... }}Copy the code

EnqueueMessage () stores messages and next() fetches messages using synchronized() locks to synchronize code

The same is true for message reading, which also takes the current MessageQueue object as a lock object to ensure the security of multi-threaded read and write.

How should we create Message when we use it?

  • One is to simply new a Message object,
  • The other is to reuse a Message that has been reclaimed by calling message.obtain (),

Of course, the latter is recommended for everyday users to get a Message, because constantly creating new objects can cause the garbage collection area to fill up and trigger the GC.

The sPool in Message is used to hold recycled messages, and when we call obtain, we first check to see if there are any reusable objects, and then create a new Message object if there are none.

Add: The main Message collection times are:

  • Remove Message in MessageQuese;
  • After we actively call the Recycle method of Message;

What does the data structure of Message look like?

A single linked list, in which Message holds a reference to the next Message object via Next

class Message{
	Message next;
}
Copy the code

Why does Looper loop death not cause apps to freeze

class Looper{ public static void loop() { final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }... }}}Copy the code

Take a look at the next code in MessageQueue:

Class MessageQueue{// Add a Message to the queue Boolean enqueueMessage(Message MSG, long WHEN) {... synchronized (this) { ... If (needWake) {// Native underlying wake nativeWake(mPtr); } } return true; } // Message next() {... for (;;) NativePollOnce (PTR, nextPollTimeoutMillis); }}Copy the code

In messagequeue.next (), the message is blocked by nativePollOnce(), and the blocked state can be woken up by the new message.

In the MessageQueue. EnqueueMessage () to add the message queue, through nativeWake ()

Simple understanding: Under normal circumstances, the stars. The loop () method in the blocking state, if there are news will perform to MessageQueue. EnqueueMessage () will put the loop (), then calls the MessageQueue. Next (message), then the loop () block

What is a HandlerThread?

HandlerThread is a subclass of Thread, which is technically a Thread, except that it creates a Looper in its own Thread

If there is insufficient, welcome to comment to supplement ~

In the previous

Blogger page

Original is not easy, your praise is my biggest support ~