In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "how to master the Handler message mechanism", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn "how to master the Handler message mechanism"!
First, the topic level
The basic principle of Handler
How to use Handler in child threads
How to wait for MessageQueue to get a message
Why use epoll instead of wait?
The relationship between threads and Handler Looper MessageQueue
Multiple threads send messages to MessageQueue, how to ensure thread safety
How to deal with Handler message delay
The difference between View.post and Handler.post
Memory leak caused by Handler
Is it true that non-UI threads cannot operate on View?
Second, detailed explanation of the topic
Code analysis is based on Android SDK 28
You can first look at the above questions and think about it. If it is clear, there is no need to read the following article.
1. The basic principle of Handler
With regard to the principle of Handler, compared to needless to say, everyone should know that a picture can be illustrated (the picture comes from the network).
two。 How to use Handler in child threads
In addition to the basic principles of Handler above, how to use Handler in child threads is also a common problem. Using Handler in a child thread requires two operations: Looper.prepare and Looper.loop. Why is it necessary to do so? What have Looper.prepare and Looper.loop done? We know that if you create a Handler directly in a child thread, the following error will be reported:
"Can't create handler inside thread xxx that has not called Looper.prepare ()
We can take a look at the constructor of Handler, which determines the Looper. If the Looper obtained through ThreadLocal is empty, the above error is reported.
Public Handler (Callback callback, boolean async) {mLooper = Looper.myLooper (); if (mLooper = = null) {throw new RuntimeException ("Can't create handler inside thread" + Thread.currentThread () + "that has not called Looper.prepare ()");}} public static @ Nullable Looper myLooper () {return sThreadLocal.get ();}
So what is done in Looper.prepare?
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));}
As you can see, Looper.prepare creates a Looper and sets it to ThreadLocal. One detail here is that there can be only one Looper per Thread, otherwise an exception will be thrown. The Looper.loop just starts to read the messages in the MessageQueue and executes it.
This usually leads to the question of why these two methods are not called manually in the main thread. As you all know, the call has already been made in ActivityThread.main. Through this question, it can be extended to the relevant knowledge of ActivityThread, so I won't go into details here.
3. How MessageQueue waits for messages
Looper.loop mentioned above is actually starting to read messages in MessageQueue, so what is Looper doing when there are no messages in MessageQueue? We know we are waiting for news, so how do we wait?
Through the Looper.loop method, we know that it is MessageQueue.next () that gets the message. If there is no message, it will be blocked here. How does MessageQueue.next wait?
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;}} Message next () {for (;;) {nativePollOnce (ptr, nextPollTimeoutMillis); / /...}}
The native method nativePollOnce is called in MessageQueue.next.
/ android_os_MessageQueue.cppstatic void android_os_MessageQueue_nativePollOnce (JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast (ptr); nativeMessageQueue- > pollOnce (env, obj, timeoutMillis);} void NativeMessageQueue::pollOnce (JNIEnv* env, jobject pollObj, int timeoutMillis) {/ /. MLooper- > pollOnce (timeoutMillis); / /...} / / Looper.cppint Looper::pollOnce (int timeoutMillis, int* outFd, int* outEvents, void** outData) {/ /. Result = pollInner (timeoutMillis); / /...} int Looper::pollInner (int timeoutMillis) {/ /. Int eventCount = epoll_wait (mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);}
As we can see from the above code, on the native side, epoll_wait is finally used to wait. The epoll_wait here is a part of the epoll mechanism in Linux. We will not introduce too much about the epoll mechanism here. If you are interested, please refer to https://segmentfault.com/a/1190000003063859.
So, at this point, there is another question: why not use wait / notify in java but use native's epoll mechanism?
4. Why use epoll instead of wait?
It is said that wait / notify in java can also block waiting for messages, and it did so before Android 2.2. You can refer to this commit https://www.androidos.net.cn/android/2.1_r2.1p2/xref/frameworks/base/core/java/android/os/MessageQueue.java, so why use epoll instead? By looking at the commit record, you need to handle events on the native side, so just using java's wait / notify is not enough. The specific change is this commit https://android.googlesource.com/platform/frameworks/base/+/fa9e7c05c7be6891a6cf85a11dc635a6e6853078%5E%21/#F0.
Sketch of Native input for MessageQueue / Looper / ViewRootMessageQueue now uses a socket for internal signalling, and is preparedto also handle any number of event input pipes, once the plumbing isset up with ViewRoot / Looper to tell it about them as appropriate.Change-Id: If9eda174a6c26887dc51b12b14b390e724e73ab3
However, select was first used here, and then changed to epoll. For details, you can see this commit https://android.googlesource.com/platform/frameworks/base/+/46b9ac0ae2162309774a7478cd9d4e578747bfc2%5E%21/#F16.
As for the difference between select and epoll, we won't go into details here. You can take a look at it in the reference article above.
5. The relationship between threads and Handler Looper MessageQueue
The relationship here is that one thread corresponds to one Looper and one MessageQueue corresponds to multiple Handler.
6. Multiple threads send messages to MessageQueue, how to ensure thread safety
Since one thread corresponds to one MessageQueue, how can thread safety be ensured when multiple threads send messages to MessageQueue? To put it simply, it just adds a lock.
/ / MessageQueue.javaboolean enqueueMessage (Message msg, long when) {synchronized (this) {/ /...}} 7. How to deal with Handler message delay
Another problem that Handler extends is how are deferred messages handled in Handler? Timer or some other way? Here, let's start with the initiation of the incident:
/ / Handler.javapublic final boolean postDelayed (Runnable r, long delayMillis) {return sendMessageDelayed (getPostMessage (r), delayMillis);} public final boolean sendMessageDelayed (Message msg, long delayMillis) {/ / the incoming time is uptimeMillis + delayMillis return sendMessageAtTime (msg, SystemClock.uptimeMillis () + delayMillis);} public boolean sendMessageAtTime (Message msg, long uptimeMillis) {/ /. Return enqueueMessage (queue, msg, uptimeMillis);} private boolean enqueueMessage (MessageQueue queue, Message msg, long uptimeMillis) {/ / call MessageQueue.enqueueMessage return queue.enqueueMessage (msg, uptimeMillis);}
From the logic of the above code, the Handler post message is called all the way to MessageQueue.enqueueMessage, and the most important step is that the time passed in is uptimeMillis + delayMillis.
Boolean enqueueMessage (Message msg, long when) {synchronized (this) {/ /. Msg.when = when; Message p = mMessages; / / next message / / sort by when and insert the message into it if (p = = null | | when = = 0 | | when < p.when) {msg.next = p; mMessages = msg; needWake = mBlocked } else {/ / find a suitable node Message prev; for (;;) {prev = p; p = p.next.if (p = = null | | when < p.when) {break }} / / insert operation msg.next = p; / / invariant: P = = prev.next prev.next = msg;} / / Wake up the queue to fetch messages if (needWake) {nativeWake (mPtr);}} return true;}
From the above code, we can see that when post a delayed message, an order is sorted in MessageQueue according to the length of the when. Then we'll see how to use when.
Message next () {/ /... For (;;) {/ / wait for messages via epoll_wait, waiting for nextPollTimeoutMillis length nativePollOnce (ptr, nextPollTimeoutMillis); synchronized (this) {/ / current time final long now = SystemClock.uptimeMillis (); Message prevMsg = null; Message msg = mMessages If (msg! = null & & msg.target = = null) {/ / get a valid message do {prevMsg = msg; msg = msg.next;} while (msg! = null & &! msg.isAsynchronous ()) } if (msg! = null) {if (now < msg.when) {/ / indicates that execution needs to be delayed. NativePollOnce's timeout to delay / / get the time to wait for execution nextPollTimeoutMillis = (int) Math.min (msg.when-now, Integer.MAX_VALUE);} else {/ / immediate execution message, directly return / / Got a message. MBlocked = false; if (prevMsg! = null) {prevMsg.next = msg.next;} else {mMessages = msg.next;} msg.next = null; msg.markInUse (); return msg }} else {/ / No more messages. NextPollTimeoutMillis =-1;} if (pendingIdleHandlerCount < 0 & & (mMessages = = null | | now < mMessages.when) {/ / currently there is no message to execute, then execute the contents of IdleHandler pendingIdleHandlerCount = mIdleHandlers.size ();} if (pendingIdleHandlerCount)
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.