In our daily development, we will inevitably use Handler. Although the Handler mechanism is not equivalent to the Android message mechanism, the Handler message mechanism has long been familiar to Android developers and is very important!
Through this article, you can easily get the answers to the following questions:
Simple use of Handler I believe that everyone should know how to use Handler, right? Suppose you are processing a time-consuming task in an Activity and need to update the UI. Let's take a look at how we usually handle it.
The above code is very simple. Because network request is a time-consuming task, we open a new thread and notify the main thread to update the UI through Handler after the network request is parsed. We simply use three methods. Careful friends may find that the first and second methods are the same. That is, Handler is used to send a Message object with content. It is worth mentioning that we should use Message.obtain() instead of new Message() to initialize Message as much as possible, mainly because Message.obtain() can reduce memory application. In response to the suggestions made in the previous article, we will try to post as little source code as possible. You can easily find that all the above methods will eventually call this method:
In the above code, a MessageQueue appears, and finally the MessageQueue#enqueueMessage method is called to enqueue the message. We have to briefly talk about the basic situation of MessageQueue. MessageQueue As the name implies, MessageQueue is a message queue, that is, a container for storing multiple messages. It uses a one-way linked list data structure instead of a queue. Its next() points to the next Message element in the linked list.
From the implementation of enqueueMessage(), its main operation is actually the insertion operation of the singly linked list. I will not explain it too much here. We should probably pay more attention to its dequeue operation method next():
The next() method is actually very long, but we only posted a small part of it. You can see that it is just an infinite loop of for (;;), and a nativePollOnce(long, int) method is called inside the loop body. This is a Native method, which actually blocks the current call stack thread for nextPollTimeoutMillis milliseconds through the Native layer's MessageQueue. The following is the blocking behavior of different nextPollTimeoutMillis values:
As you can see, the initial value of nextPollTimeoutMillis is 0, so it will not block and will directly fetch the Message object. If the Message object data is not fetched, nextPollTimeoutMillis will be set to -1 directly. At this time, if the condition of less than 0 is met, it will be blocked until another Native method nativeWake(long) is called elsewhere to wake up. If a value is fetched, the obtained Message object will be directly returned. It turns out that the nativeWake(long) method has a call to the previous MessageQueue#enqueueMessage method, and the call is made during the process of enqueuing a message in the MessageQueue. Now we know that Handler sends Message, MessageQueue stores it, MessageQueue#enqueueMessage is used to enqueue it, and MessageQueue#next is used to poll messages. This inevitably raises a question: who calls the MessageQueue#next method? Yes, it is Looper. Looper Looper plays the role of a message loop in the Android message mechanism. Specifically, it will constantly check whether there are new messages from the MessageQueue through next(). If there are new messages, they will be processed immediately, otherwise the MessageQueue will be blocked there. Let's take a look at the most important method of Looper: loop():
The method omits a lot of code and only retains the core logic. As you can see, the Looper object is first obtained through the myLooper() method. If the Looper returns empty, an exception is thrown directly. Otherwise, it enters a for (;;) loop and calls the MessageQueue#next() method to obtain the Message object in turn. If the obtained Message object is empty, it exits the loop() method directly. Otherwise, the Handler object is obtained directly through msg.target and the Handler#dispatchMessage() method is called. Let's first look at the Handler#dispatchMessage() method implementation:
The code is relatively simple. If the Message has a callback set, it directly calls message.callback.run(). Otherwise, it checks whether the message has been initialized. Let's take a look at the myLooper() method:
Let's take a look at what sThreadLocal is:
What is this ThreadLocal? ThreadLocal Regarding ThreadLocal, we directly adopt the content in Yan Zhenjie's article. The first impression of seeing ThreadLocal is that this class is related to threads, which is indeed the case, but it should be noted that it is not a thread, otherwise it should be called LocalThread. ThreadLocal is used to store data of a specified thread. When the scope of some data is the specified thread and the data needs to run through all execution processes of the thread, ThreadnLocal can be used to store data. When a thread uses ThreadnLocal to store data, only the thread can read the stored data, and other threads except this thread cannot read the data. Some readers may still not understand the role of ThreadLocal after reading the above paragraph. Let's take an example:
There is nothing much to say about the code. You will see the following printed log:
The first log is unquestionable because the value is set to true, and there is nothing to say about the printed result. For the second log, according to the above introduction, the data stored in a thread using ThreadLocal can only be read by the thread, so the result of the second log is: null. Then the value of ThreadLocal is set to false in the child thread, and then the third log will be printed. The principle is the same as above. The value of ThreadLocal set in the child thread does not affect the data of the main thread, so the print is true. The experimental results confirm that even for the same ThreadLocal object, the operations of the set() and get() methods of any thread are independent of each other and do not affect each other. Looper.myLooper() Let's go back to Looper.myLooper():
Let's see where sThreadLocal is operated.
So you know, this is why you must call Looper.prepare() before using Handler in the child thread. You may wonder, when I use it in the main thread, I don't require Looper.prepare(). It turns out that we have explicitly called Looper.prepareMainLooper() in ActivityThread:
Let's look at Looper.prepareMainLooper():
|
<<: SiriOS may be released soon to help Apple's smart home ecosystem
>>: Simple mode returns to the original intention of mobile QQ new internal beta experience
The first draft of this Zhihu content operation m...
If you only want to see the conclusion (pitfall),...
Introduction to WebView WebView is a control used...
In the Internet age, image display ads are becomi...
On December 12 , the Baoding Traffic Congestion Di...
In fact, I have wanted to write an article about ...
Türkiye's discovery of nearly 700 million ton...
How to do the preliminary promotion for a newly l...
With the continuous development of the Internet, ...
Training course content: "365 Days of Childr...
This feature, called "Dynamic Perspective&quo...
This Saturday, driverless cars were put into tria...
The functions of the smartphones we use nowadays ...
Early this morning, Apple released the new genera...
Training course content: It covers every aspect o...