The main points of

Difficult points:

  1. MQnext()Method,enqueueMessage()Methods, as they are associated with Looper and MQ in the Native layer.

Key points:

  1. How messages are distributed
  2. Next () method
  3. How to quit
  4. How handlers correspond to threads

The title

  • Handler implementation mechanism (many details need to be paid attention to: how threads create and exit message loops, etc.)
  • In terms of handlers, what thread is the new Handler anywhere?
  • How does looper start when Handler sends messages to the child thread?
  • When creating a Handler in a child thread?
  • How do I create a Looper in a child thread?
  • Why do we use handlers to switch threads?

There are four main objects in the Handler mechanism: Handler, Message, MessageQueue and looper. Handler is responsible for sending and processing messages. Message is a Message object, similar to a node in a linked list; MessageQueue is a MessageQueue, which is used to store the data structure of message objects. Looper is the handler of the message queue (used to poll the message object of the message queue and call back the handler’s dispatchMessage() to distribute the message after fetching it. The dispatchMessage() method calls back to the handleMessage() method to pass in the message, which is handled by the Handler’s implementation class.

When we call new Handler() in a thread, we create the Handler using the current thread’s Looper. The Looper of the current thread exists in the ThreadLocal variable. Before using Handler, we need to call looper.prepare () to instantiate Looper of the current thread and place it in the thread local variable of the current thread (only once, it will be fetched from TL first and used later, then the constructor of Looper will be called. And initialize MQ in the constructor), and then call looper.loop () to start the message loop. The same is true for the main thread, except that the Looper of the main thread is instantiated in the ActivityThread’s main() method. We can use the looper.getMainLooper () method to get the Looper of the main thread and use it to create a Handler so that we can send messages to the main thread from any thread.

    Looper.prepare(); // Internally, Looper's new method is called to instantiate Looper and put it into the TL
    new Handler().post(() -> /* do something */);
    Looper.loop();
Copy the code

When Looper is instantiated, a MessageQueue is instantiated, At the same time, MessageQueue will call methods of the Native layer to instantiate a MessageQueue at the Native layer and use epoll between Looper of the Java layer and Looper of the Native layer To communicate. When Looper’s loop() method is called, a loop is started to process the message. When there is no message in MQ at the Java layer, Looper at the Native layer will put it to sleep and wake it up when a message arrives to process it to save CPU.

Why doesn’t opening an infinite loop in Looper’s loop() result in ANR for the main thread? This is because The Android system itself is based on messages, which are messages sent to the main thread. ANR is generated not because the tasks in the main thread are infinite, but because the infinite loop causes other events not to be processed.

Android Message Mechanics: Handlers, MessageQueue, and Looper

Handler memory leaks and solutions: If the handler is not a static inner class, the handler holds an anonymous reference to the Activity. When an Activity is to be reclaimed, the reference to the Handler Activity cannot be released, causing the Activity to remain in memory without being reclaimed, causing a memory leak.

1). Set Handler to a static inner class. 2). Make the Handler hold a weak reference to the Activity; Call the handler.removecallBack () method in the Activity lifecycle onDestroy().

  • Why can’t I access the UI in a child thread?

Controls in Android are not thread-safe and are designed to be: 1). Designed to be synchronous to simplify the complexity of use; 2). Improves control performance (asynchronous locking is an additional overhead in non-multithreaded environments).

  • Is it the Looper thread or the Handler thread that determines which thread the handler.post () logic is executed on?The thread on which the Handler is called refers to the thread on which the Handler is calledpost()Method is the thread on which the Handler resides.)
  • Handler post()/ Send ()
  • What are the similarities and differences between Handler’s post() and postDelayed() methods?

The thread on which the post() method resides is determined by the thread on which Looper resides; The final logic is in the looper.loop () method, which takes the Message from MQ and executes its logic. This is performed in Looper. Therefore, this is determined by the thread in which the Looper resides.

Whether you call a send() method or a post() method, you’ll end up calling sendMessageAtTime(). The difference between POST () and postDelay() is that the former uses the current time, while the latter uses the time of the current time +delay to determine when a message is triggered. The final method parameters will be wrapped as a Message object and added to the Handler’s corresponding Looper MQ to be executed.

  • Do Looper and Handler have to be on the same thread? Can MainLooper be used to create Handler in a child thread?

The Looper and Handler do not need to be in the same thread. By default, the Looper of the current thread is fetched from the TL, but we can create a Handler by explicitly specifying a Looper. For example, if we want to send a message from a child thread to the main thread, then we can

Handler handler = new Handler(Looper.getMainLooper());
Copy the code
  • Does the handler.post () method send a synchronous message? Can I send asynchronous messages?

The user layer sends only synchronous messages and cannot send asynchronous messages. Asynchronous messages can only be sent by the system.

  • Messagequeue.next () blocks because a delayed message has been found. So why aren’t the non-delayed messages added later blocked?
  • MessageQueue. EnqueueMessage () method of the principle of how to carry out the thread synchronization?
  • What’s inside the messagequeue.next () method?
  • How does next() handle general messages?
  • How does next() handle synchronization barriers?
  • How does next() handle delayed messages?

When the messagequeue.next () method is called, the nativePollOnce() method of the Native layer is called for precise time blocking. At the Native layer, you go to the pullInner() method and use epoll_WAIT to block the wait to read the pipe’s notification. This method will not return if the message is not received from the Native layer. At this point, the main thread releases CPU resources and enters the hibernation state.

When we join Message, will call MessageQueue. EnqueueMessage () method, after adding the Message, if the Message queue blocked, will call Native layer nativeWake () method to wake up. It ends the blocking by writing a Message to the pipe, triggering the nativePollOnce() method mentioned above to return so that the added Message can be distributed.

MessageQueue. EnqueueMessage () USES synchronized code block to synchronize.

Data: Research on Native layer of Handler in Android

  • The HandlerdispatchMessage()The process of distributing messages?

When we use a Handler, we override the handleMessage() method of that Handler. When we call this Handler’s send() or post() to send a Message, the Message is wrapped as “Message” and the target of the Message is pointed to the current Handler. This message will be put into Looper’s MQ. Then in the Looper loop, we take the Message and call its target Handler, which is the Handler’s dispatchMessage() method to process the Message. The Handler’s handleMessage() method is called to process the message and the Callback is called.

  • Why does Handler need a Callback constructor?

The Handler’s dispatchMessage() method Callback is called directly when the Handler is executed in the message queue.

  • Handler constructorLooper.myLooper()How do I get Looper for the current thread?

Get from TL

  • What is the underlying queue used in MessageQueue?

It’s a single linked list, not a queue

  • Looper’s two exit methods?
  • What’s the difference between quit() and quitSafely()
  • How to terminate the message loop after a Looper is created in a child thread?
  • What is the essence of quit() and quitSafely()?

The essence of quit() and quitSafely() is to quit looper.loop () by having next() of the message queue return null.

Quit () directly terminates Looper, no Message is processed, and all attempts to put messages into the Message queue will fail. For example, Handler. SendMessage () will return false, but it is unsafe. The Looper is terminated because there may be a Message in the queue that has not been processed.

The quitSafely() call will not terminate Looper until all messages have been handled, and any attempt to put the Message on the Message queue will fail.

    public void quit(a) {
        mQueue.quit(false);
    }
    public void quitSafely(a) {
        mQueue.quit(true);
    }
    void quit(boolean safe) {
        if(! mQuitAllowed)throw new IllegalStateException("Main thread not allowed to quit.");
        synchronized (this) {
            if (mQuitting) return;
            mQuitting = true;
            if (safe)  removeAllFutureMessagesLocked(); // Clear all delayed messages
            else       removeAllMessagesLocked();  // Empty the message queue directlynativeWake(mPtr); }}Copy the code
  • Under what conditions does looper.loop () exit?

1).next() returns MSG == null; 2). Thread terminates unexpectedly.

  • Looper.loop() source process?
  1. Looper and message queue are obtained.
  2. For infinite loop that blocks the message queuenext()Methods;
  3. Called after the message is retrievedmsg.target.dispatchMessage(msg)Message distribution.
  • Looper.loop()Method is executed if the internalmyLooper()What happens if you can’t get Looper?

abnormal

  • How does Android ensure that a thread can have at most one Looper? How to ensure that there is only one MessageQueue

Looper is created using looper.prepare () before Handler is used in a thread. It will be fetched from the TL. If Looper already exists in the TL, an exception will be thrown.

  • How does a Looper distinguish between multiple handlers in the Handler messaging mechanism?

Looper does not distinguish between handlers, depending on the Message distribution mechanism. Each Handler is added to the target field of the Message. Stars by calling Message. Target. HandleMessage () Handler to process the Message.

The last

Follow the author for more advanced interview answers