Introduction:
The Android messaging mechanism mainly refers to the operation mechanism of the Handler. From a development perspective, Handler is the upper interface to the Android messaging mechanism. The Handler is supported by MessageQueue and Looper.
The main content
- An overview of Android messaging
- Android message mechanism analysis
- Main Content 3
The specific content
- MessageQueue is a MessageQueue, which stores a group of messages in the form of queue to provide external insert and delete work, and uses a single linked list data structure to store the message list.
- Lopper looks in an infinite loop to see if there are any new messages, processes them if there are, and waits otherwise.
- ThreadLocal is not a thread; it stores data in each thread. The Handler uses ThreadLocal to retrieve Looper from each thread.
- Threads do not have Looper by default, so using Handler you must create Looper for the thread. The oft-mentioned main thread, also known as the UI thread, is an ActivityThread that initializes Looper when it is created.
An overview of Android messaging
- The Handler is used to switch a task to the thread where the Handler resides. Why does Android offer this feature? This is because Android states that the UI can only be accessed through the main thread, and if a child thread accesses the UI, the application may cause ANR. So what we’re doing is we need to switch some of the UI updates to the main thread after the child thread completes. So the system provides a Handler.
- Why doesn’t the system allow access to the UI in child threads? Because Android UI controls are not thread-safe, concurrent access by multiple threads may cause UI controls to be in an unexpected state. Why not lock them? Because locking can complicate UI access logic; Second, locking reduces the efficiency of UI access because it blocks the execution of certain threads. So Android uses an efficient single-threaded model to handle UI operations.
- Handler is created to build the internal message loop using the current thread’s Looper, and an error is reported if the current thread has no Looper. The Handler can either send a Runnable to the message queue using post or send, which is also used to send the message queue.
- MessageQueue’s enqueueMessage method finally puts the message into a MessageQueue. When Looper detects that a new message has arrived, it processes the message. The final message’s Runnable or Handler’s handleMessage method is called. Note that Looper is in the thread in which the Handler is running, so the business logic is switched to the thread in which the Handler is running.
Android message mechanism analysis
How ThreadLocal works
ThreadLocal is an internal data store class that stores data in a specified thread. Once the data is stored, only the specified thread can retrieve the stored data. Other threads cannot retrieve the data.
ThreadLocal can easily implement complex functions in specific scenarios. Looper, ActivityThread, and AMS all use ThreadLocal. Consider using ThreadLocal when some data is thread-scoped and different threads have different copies of the data.
In the case of Handler, it needs to fetch the Looper of the current thread. Looper is used by the same thread and different threads have different Loopers.
Another use of ThreadLocal is to allow listeners to exist as global objects within a thread, where they can be obtained by simply using the GET method. Without ThreadLocal, you can only use function parameter calls and static variables. While the first approach is bad when the call stack is deep, the second approach is not scalable, such as multiple threads executing at the same time.
Although the same ThreadLocal object is accessed on different threads, the values obtained are different. When different threads access the same ThreadLoacl’s GET method, the ThreadLocal’s GET method will fetch an array from each thread and then search for the corresponding Value from the array based on the current ThreadLocal index. ThreadLocal’s set method:
public void set(T value) {
Thread currentThread = Thread.currentThread();
// Get ThreadLoacl data from the current thread with values -- localValues
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
Copy the code
- There is an array inside localValues: Private Object[] table, in which ThreadLocal values are stored.
- The value of a ThreadLocal is always stored in the table array next to the object identified by the Reference field of a ThreadLocal.
ThreadLocal get method:
public T get(a) {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);// Find localValues object
if(values ! =null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {// Locate ThreadLocal's reference object in the table array
return (T) table[index + 1];// The next location of the object identified by the reference field is the value of ThreadLocal}}else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
Copy the code
The set/get method of a ThreadLocal is a table array of the localValues object of the current thread. Therefore, the set/get method of a ThreadLocal is accessed by different threads. Read/write operations of their ThreadLocal are limited to the interior of their respective threads. Understanding how ThreadLocal is implemented helps you understand how Looper works.
How message queues work
A MessageQueue refers to a MessageQueue and consists of two operations: insert and read. The read operation itself is accompanied by a delete operation.
MessageQueue internally maintains a list of messages through a singly linked list data structure, which provides performance advantages for inserts and deletions.
The corresponding insert and read methods are enqueueMessage and next, respectively. The source code implementation for enqueueMessage() is the insertion of a single linked list. The source code implementation for Next () is the extraction of an element from a single linked list. The next() method is a wireless loop, and if there is no message in the message queue, the next method will block. When a new message arrives, the next() method returns the message and removes it from the singly linked list.
How Looper works
Looper acts as a message loop within Android’s messaging mechanism, specifically that it constantly checks for new messages from MessageQueue, processes them immediately if there are new messages, and otherwise blocks them.
Create a Looper for the current thread with the looper.prepare () method, and start the message loop with looper.loop (). The prepareMainLooper() method is used to create loopers for the main ActivityThread and is essentially implemented by using the prepare method.
The Looper provides the quit and quitSafely command to quit a Looper. The difference is that quit will quit the Looper directly, while the quitSafely command handles all the messages already in the message queue before quitting safely. After Looper exits, messages sent by Handler will fail, and Handler’s send method will return false. In a child thread, if you manually create a Looper for it, you should call The Quit method of Looper to terminate the message loop after everything is done, otherwise the child thread will remain in a waiting state. The thread will terminate immediately after exiting the Looper, so it is recommended to terminate the Looper when it is not needed. The loop() method calls MessageQueue’s next() method to get a new message, and next is a blocking operation, but the next method blocks until there is no information, which causes the loop method to block. If MessageQueue’s next method returns a new message, Looper processes the message: MSG. Target. DispatchMessage (MSG), the MSG here. The target is to send the message Handler object, so the Handler to send messages and eventually to the Handler to deal with.
How this Handler works
The Handler sends and receives messages. Through a series of methods post and send to achieve a series of methods.
The Handler sends simply by inserting a message into the message queue. MessageQueue’s next method returns the message to Looper, which takes the message and starts processing it. Finally, the message is sent to Handler’s dispatchMessage(), at which point the Handler enters the message processing phase.
Handler The process of processing a message:
Handler constructor:
- Derive a subclass of Handler.
- Through the Callback. Handler Handler = new Handler(callback), where the callback interface is defined as follows:
public interface Callback{
public boolean handleMessage(Message msg);
}
Copy the code
- Through the stars.
public Handler(Looper looper){
this(looper,null.false);
}
Copy the code
Message loop for the main thread
Android’s main thread is ActivityThread. The main thread entry method is main(String[] args), PrepareMainLooper () creates the main thread’s Looper and MessageQueue, and starts the main thread’s message loop with looper.loop ().
ActivityThread communicates with AMS through the ApplicationThread. The Binder method is called back by AMS after the ApplicationThread completes its request. The ApplicationThread then sends a message to H. Upon receiving the message, H switches the logic in the ApplicationThread to ActivityTread, that is, to the main thread for execution. The startup process of the four major components is basically this process.
Looper.loop(), which is an infinite loop, and the application throws an exception if the main thread’s Looper terminates. If the main thread is stuck here, (1) why can the Activity start? (2) Click a button and still respond?
Problem 1: When startActivity is started, AMS (ActivityManagerService) sends a cross-process request (AMS runs in the system process), and AMS starts the corresponding Activity. AMS also calls the App Activity lifecycle method (which cannot be called directly by different processes). AMS sends a cross-process request, which is then handled by the ApplicationThread in the App’s ActivityThread. The ApplicationThread switches execution logic to the main thread through the Handler of the main thread. The main thread Handler adds the message to MessageQueue, and Looper. Loop picks up the message and executes it on the main thread. This explains why the main thread Looper is an infinite loop, but the Activity can still be started because all four component lifetimes are sent as messages through the UI thread Handler, executed by the UI thread Looper.
Problem 2: As in problem 1, clicking a button is eventually sent by the system through looper.loop (). Question 2 for detailed analysis, please refer to the source of MotionEvent and ViewRootImpl in the Android of the author of the original book.