What about the message mechanism Handler function? What are the elements? What is the process? Tell me briefly what you think!

Handler is a mechanism for sending a task to a particular thread to execute Message Handler is responsible for sending messages,messageQueue is responsible for storing and retrieving messages,Looper is a polling, Continually fetching messages from the queue to the corresponding handler for execution Create a Looper and MessageQueue bound to the thread by calling looper.prepare (), start polling by calling looper.loop (), and check whether there is an executable message by calling messagequeue.next (). Handler Sendmessage actually calls MessageQueue’s enqueue() method to insert the message into its own linked list of messages. When the available message is retrieved, it is handed to the corresponding handler via message target and handleMessage () is executed

Why can a thread have only one Looper, only one MessageQueue, and multiple handlers?

The Looper constructor is private, and a Looper can only be initialized using its static prepare() or prepareMain() methods. The ThreaLocal method creates a new Looper and stores the Looper into the ThreadLoclMap variable of the current thread. MessageQueue is created in the Looper constructor. Loopers can only be created once in a thread. A Handle is responsible for sending and executing messages. Different handlers have different logic for processing messages. Therefore, there can be multiple handlers

How do I use handlers in child threads?

Since child threads do not have Looper by default, to use handler on child threads, call looper.prepare () to create Looper and messageQueue, and then call looper.loop () to enable polling. When you finally create a handler, you need to pass in looper as a construction parameter and bind it to use it. Because a thread is an executable piece of code, it will be destroyed after execution, but calling looper.loop () will enable polling, so the thread cannot be destroyed. Call looper.quie () to exit the polling and terminate the thread when it is no longer needed

The role of the MessageQueue

MessageQueue functions to manage message storage (enqueueMessage) and fetch (next), and acts as a bridge between Java layer and Native layer. Through AIDL, MessageQueue of native layer is linked to realize mutual invocation between Native layer and Java layer.

Next method logic

for(:; NativePollOnce (mPtr, nextPollTimeoutMillis), which blocks the main thread waiting for nextPollTimeoutMillis, or wakes it up with nativeWake () when a message arrives. The first time next () is called, nextPollTimeoutMillis=0, which is not blocked. If message.target =null, the synchronization barrier is enabled, and the synchronization Message is skipped. If message.target =null, the synchronization barrier is enabled, and the synchronization Message is skipped. Find the next asynchronous message in the linked list

If the executable message is empty, or the execution time is not up (set nextPollTimeoutMillis=when-now), check whether IdleHandler has been added to MessageQueue, and if so, It iterates through all idleHandlers and executes its queueIdle() method, then resets nextPollTimeoutMillis=0 because the message’s execution time has reached after executing IdleHandler. If IdleHandler is not added, continue is called directly to return the loop and nativePollOnce(mPtr, nextPollTimeoutMillis) is executed to block the main thread.

How does the nativePollOnce method block the main thread

Pipe and Epoll multiplex Liunx Pipe and Epoll multiplex Liunx Pipe and Epoll multiplex

The first thing to understand is, the system offers you a function, when a socket can be read or write, give you a call, this time with a nonblocking socket, only when the system informed me that the descriptor can be read, I didn’t go to for the read operation, to ensure that each time you perform a read to read the data effectively, Instead of going back to -1 and doing nothing. This function of the operating system is realized by functions such as SELECT /poll/epoll. It can monitor the read and write status of multiple descriptors at the same time, so that THE I/O operations of multiple descriptors can be completed concurrently and alternately in a thread. The reuse here refers to the reuse of the same thread.

One such implementation, Epoll, can listen on multiple FDS, with three important functions

Epoll_create () creates an epoll instance and returns the corresponding fd epoll_ctl () registers the descriptor to listen on and the event to listen on. Epoll_wait () waits for the descriptor to have an event available, and if none is currently available, blocks the calling thread until the event is available or times out

Anonymous pipes (Pipe) is a method of communication between Liunx threads or processes. The Pipe (fd[]) function creates a file descriptor for the Pipe, representing the read and write ends of the Pipe. When Epoll and Pipe are used together, FDS and events are registered with Epoll and wait() is called to wait for the event to become available. When the event becomes available, wait() returns and performs the corresponding operation. Such as listening for read events, when data is written to the write end of the pipe, the read event is available, wait() returns, and the calling thread changes from blocking to running, and the read () function can be called to read data from the pipe.

The constructor of MessageQueue will be nativeInit(), which is a native method. This method will create the MessageQueue of the native layer and assign the value to the Java layer mPtr. While creating a Native layer Looper, the Looper constructor does several important things

1. Create a read-write pipe fd (mWakeReadFd/mWakeWriteFd) 2. Call epoll_create() to create an epoll listening pool 3. Call epoll_ctl() to register the read-side FD and read event of the pipe

By doing so, epoll is ready to listen for read data events on the read side of the pipeCalling the next () method will call nativePollOnce() when retrieving the message, = finally calling the pollOnce () method of the native layer Looper, which blocks the main thread by calling epoll_wait() to wait for the readable notification of the read pipe. The main thread wakes up when it knows that the write pipe is writing or waiting for a timeoutSo when will data be written to the write pipe to wake up the main thread? The answer is MessageQueue’s nativeWake () method, which eventually calls nativeLooper’s Wake () method, writing a character “1” into the pipe by calling write(), and epoll’s wait() returns to wake up the main thread To sum up, the main thread blocking of the message mechanism is implemented by the Epoll mechanism of the Liunx system. The Epoll mechanism listens on a read/write pipe. When there is no message in the message queue, the epoll_wait() is called to block the main thread. The system notifies you of readable data, and the epoll_wait() function returns, waking up the main thread.

How is the synchronization barrier implemented

By calling the MessageQueue. PostSyncBarrier () can open the message queue synchronization barrier, open, after the next () a message will skip a synchronous message, asynchronous messaging is not affected

Msg. target==null is an open synchronization barrier. Take a look at the implementation of postSyncBarrier()The logic is simple: create a Message with an empty target and insert the Message into the head of the Message list. If there is a message.target==null in the list of messages, the synchronization barrier is enabled

What are the Handler memory leaks? What is the cause of the memory leak? How do I fix memory leaks caused by handlers?

A message sent by a handler holds a reference to an external class (usually an activity), which is a reference to an external class (usually an activity) that is held by a handler as a non-static inner class or an anonymous class. If a message is not executed in the message queue, and the external class that the handler holds is destroyed, that external class will be leaked. The solution is to not let the handler hold a reference to the external class, You can use either an external separate Handler or a static inner class Handler, using weak reference wrapping when you want to use an external class, Finally, at the end of the external class declaration cycle, messagequeue’s removemessageAndCallback () method is called to clean up unexecuted messages

How does the main thread automatically bind Looper? What are the differences between Looper dead-loops in the main thread and those in binder threads?

At startup, ActivityThread is loaded into the process and its main() method is executed. Looper and looper.loop are used to create the main thread Looper and enable polling. //TODO Binder]

Why is it not recommended to access the UI in child threads and not to lock UI controls?

Because UI controls are not thread-safe, concurrent access across multiple threads can cause the UI to be in an uncontrollable state for locking

  • Reduce the efficiency of UI access
  • Adding locks in the first place complicates the logic of UI access

So the best approach is to use a single-threaded model

Looper.loop is an infinite loop that blocks if it doesn’t get the Message it needs to process. Why doesn’t it cause ANR or lag in the UI thread?

The essence of lag is that there is no drawing work within the frame refresh time (60ms). When the next SYN signal arrives, the user sees the content of the last frame. The user feels that the interface is blocked because the event processing time is too long. So the essence of a thread is a piece of code that can be executed, and when the code is executed, the thread declares that the period is terminated, and the thread exits, but for the main thread it can’t execute and the code exits, and the easiest way to do that is to block and hibernate when there is no message. The Android system is event-driven, and the common click-and-touch declaration cycle control is accomplished by the handler mechanism. If the main thread is blocked, there are two reasons for anR

  • The event cannot be handled in a timely manner
  • The event processing time is too long

How to realize delay Handler. SendMessageDelayed ()?

Messagequeue.enqueue () will be inserted into the linked list of message queues in descending order according to the when attribute of message, i.e. the execution time of the message. In the method of retrieving messages, next() will compare the current time and the execution time of the message to extract the messages that can be executed currently

How do YOU store messages

MessageQueue. Enqueue () will be called, traversing the list and inserting the message into the appropriate location according to the message execution time (systemclock. uptimeMillis()+delayTime). Equivalent to inserting an element 😺 into an ordered linked list

How can messages be created? Which works better and why?

You can either simply new one or call the message.obtain() method. The second option is recommended because it returns an instance from the message pool and avoids creating new objects

What does MessageQueue do? How does MessageQueue work?

MessageQueue is responsible for storing and retrieving messages. There are two main methods: Enqueue () and Next (). Enqueue () inserts messages into the internal list in chronological order of execution. To the message target, the handler that sends the message

What does ThreadLocal do?

Each thread object contains a ThreadLocalMap object. The key is the thread itself, and the value is the object. ThreadLocalMap is similar to hashMap

ThreadLocal is a utility class. The get and set methods fetch the ThreadLocalMap of the calling thread and then call the GET and PUT methods of ThreadLocalMap. The key of a ThreadLocalMap is always the thread itself

This ensures that only one Looper exists in the same thread

IdHandler

By messageQueue. AddIdleHandler () method to add, its queueIdle () method will be no message in the message queue, preparation before entering the thread block is called, returns false performs a removed after oneself, return true can be repeated in every time they meet the above conditions

After the first frame draws the message, the added idleHandler will be executed. Using this feature, it can be used to count data from the application? The surface is visible on the first frame

Child threads must not refresh the UI, okay?

A thread check is performed in the requestLayout of the ViewRootImp to check whether the current thread matches the thread that created the ViewRootImp. The ViewRootImp is created when windowManager.addView is called, so child threads can update the UI in the following 2 cases

  • Have the ViewRootImp created in a child thread and then call the interface refresh method in the same thread, although I don’t know why?
  • The interface refresh is not designed to be remeasured or laid out, which means that requestLayout is not called and thread checks, such as setTextColor, are not triggered