当前位置:网站首页>What exactly is a handler
What exactly is a handler
2022-06-25 16:00:00 【Favorite grapes】
Handler What exactly is it
As a Android Development Engineer ,Handler It is absolutely necessary to know . Before each interview ,Handler Would sneak into my ear and say :“ well , Brother , See you in the old place !”
Sure enough , The interview asked again , and Handler Come here again BB:“ I'm embarrassed again !”
( Inner monologue , I don't believe I can't handle you !)
So there is this analysis .
Let's start with a few questions
- You know Android Of Handler Mechanism ?( I said with a smile , That must be understood , After a colloquial conversation, we have the following questions )
- You know, Handler On Android framework Are there any applications in ?
- You know, Handler Synchronization barrier in ?
- Why are you passing
view.post()Acquired view Width and height are accurate ?post Won't the message appear after the measurement process ? - Balabalabalabalabala
All right , When I heard the first question, I was full of confidence until I was discouraged by the last question , Directly hit by the penetrating .
Why should I know so much , Just know how to write ?
It's true , I will use handler Not to go ? As a painting UI Of , Do I need to know so much ?
But my understanding is that , The more you understand the bottom layer , Write bug The less likely it is , Because I know what happened . There is also the ability to understand capability boundaries , know Handler What can and cannot be done , This is very helpful for developing other technical solutions .
Don't talk much , Next , Will the 「Handler」 This son of a bitch has a good steak .
Catalog
- Android Message processing system in
- Handler How it turns
- Handler stay Android Figure in the system
- Handler The ability boundary of ( What can this son of a bitch do )
Android Message processing system in
In many desktop operating systems , All have their own Message processing framework , such as Windows、Android.
So the most basic , Message processing framework , At least there must be a message sender (handler)、 The receiver of the message (handler)、 The message itself (Message).
When message production is very fast , You also need a storage party to temporarily cache messages (MessageQueue).
When the message does not go directly to the target itself , A dispatch center in the middle is required (Looper), Process messages separately , Convenient for unified dispatching .
Here is a practical example : Express delivery
- The sender packs the goods , Give the package to the express company .
- The express company loads the packages that many people want to send to the target city .
- When the express arrives , Then deliver the packages to the corresponding recipients
Of course, someone said that , I'm going to flash , Only for me at one time ! Then I can only say , Your pen .
In the case above :
- handler It is equivalent to a courier . Be responsible for receiving 、 Express delivery .
- Message It's a courier . Of course, express delivery is also divided into ( Express delivery 、 Ordinary express delivery 、 Empty bag )
- MessageQueue It is a warehouse for express delivery
- Looper It is used to distribute each express to the corresponding courier , It can be used as an express company .
- The sender is the thread A
- The recipient is the thread B
Yes, of course handler Not limited to inter thread communication .
Handler How it turns
tips: be based on API 30
Next, let's take a simple picture to show Handler How it turns .

Although this diagram is not very standard , But it does describe Handler A whole working mechanism of .
Then we will know , In fact, except Handler There are several other roles besides , Let's show them here .
- Looper: Used to start the cycle , Process the message , It is equivalent to a big manager .
- MessageQueue: Store messages ( In fact, it is mainly the operation Message Linked list ), Read message
- Message: Encapsulation of messages , Can communicate across processes , In essence, it is a one-way linked list structure , And will hold Handler( The goal is handler, In fact, they are sending messages Handler In itself ).
- Handler: A courier for sending and processing messages .
So what is their real workflow ?
How to use Handler
thread {
// 1、 establish Looper, Must have created Looper, To create Handler
Looper.prepare()
// 2、 establish Handler
handler2 = Handler()
// 3、 Give Way Looper Enter the cycle state
Looper.loop()
}.start()
The following steps are summarized :
- establish Looper,Looper.prepare().
- establish Handler, Used for sending and receiving messages .
- Into the loop ,Looper.loop().
Next step by step , Deep source , Analyze .
establish Looper
Want to send a message , There has to be one first Looper Talent . So why do you have to Looper.prepare();
Let's go deep into the source code .
Find out Looper There is only one private constructor :
private Looper(boolean quitAllowed) {
// establish MessageQueue, Whether incoming data can be exited , what ? There are still things that cannot be exited ?
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Mr. , original Looper There is no method to create objects directly ,( How about I create one by myself through reflection ! You can try !)
So let's see prepare Method
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// Determine whether the current thread already exists Looper object , If there is an error .
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// establish Looper Object and put it into the internal static object sThreadLocal in .
sThreadLocal.set(new Looper(quitAllowed));
}
from prepare Methods found , original Looper Provided this prepare Method , Easy for developers to create Looper object , So why should it create itself , And only one thread is allowed to be created ?
Looper It is an endless loop to get messages , So it doesn't make sense to create multiple , It's just a waste of space . And here we see the passing in quitAllowed The default is true, And transitive parameters prepare The method is private , There is no way to call directly to .
Inside again ThreadLocal Object stored Looper object , It is convenient for other methods in the thread to pass directly Looper.myLooper() Get objects .
establish Handler
Handler Unlike Looper, It mainly gets the current thread by Looper Or designated Looper, To send messages .
So create Handler when , You can specify Looper, Or not ( When not specified , The current thread summary must have Looper object ),
It is called. Handler Construction method of ( Passive transmission Looper object ) after , Finally, it will be called as follows Construction method :
public Handler(@Nullable 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()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Here we find Handler There will be one in the constructor of async Parameters , When this parameter is true when ,handler When the method to send the message is called , In the end Message Set to asynchronous message .
if (mAsynchronous) {
msg.setAsynchronous(true);
}
However , As App developer , We have no direct interface to set this parameter , Then we can also pass Message Set asynchronous messages directly and manually .
Message().apply {
// But this method is in API22 You can use it later
isAsynchronous = true
}
Enter the cycle state
When calling the Looper.loop(); The whole message processing has been completed .
Wait a minute and you'll get through , What are you talking about MessageQueue Well ? Doesn't that thing need to be created ?
MessageQueue In fact, it will be in Looper It has been created at the time of creation , As Looper The member of the object exists .
that loop What did you do ?
public static void loop() {
// Determine whether the has been created Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
final MessageQueue queue = me.mQueue;
// Into the dead cycle
for (;;) {
// Take the message from the message queue .
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// Get the observer , Why , What is this observer ? Can I add an observer ? No, at least not in the conventional way . Because it is @hide Of
final Observer observer = sObserver;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
try {
// After getting the message , adopt msg Medium target Distribute messages .
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// Recycling messages
msg.recycleUnchecked();
}
}
From the above code we can see ,Looper.loop() The most essential behavior , Just keep passing queue.next(); from queue In order to get Message And deal with .
that queue.next What is so special about this method ?
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// ① Into the dead cycle , Eh, why is this a dead circle ?
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//② Here is the waiting state , If you are in a waiting state, why not wait()? While using native Of epoll Method ?
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// ③ If target == null, Then enter the cycle , Yes? target It can be for null Do you ? Not through Handler Is it set by default when sending messages ?
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
// ④ If msg Not empty , And not an asynchronous message , Mr. , It turns out that the purpose here is to find asynchronous messages .
} while (msg != null && !msg.isAsynchronous());
}
// ⑤ If the message is not empty , Then I know , The corresponding message must be returned .
if (msg != null) {
// ⑥ Yes, it still needs time to judge , What about other messages that I have set delay ?
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
// Only when the asynchronous message is found will it not be null Well
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// Let the next one do the head Message 了
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
// If you want to quit , Just quit .
if (mQuitting) {
dispose();
return null;
}
……
}
}
For the code above , Although we can all understand , But there are still questions , Next, let's analyze these problems .
① Into the dead cycle , Eh, why is this a dead circle ?
We know that Looper in , There has been an endless cycle , Is it not enough to go through that dead circle ? In my opinion , It should be done directly in the dead circle , If there is Message Is executed , If it doesn't exist, just exit .
First of all, you can't just quit , What if there's news coming later , Therefore, you must not exit directly here . So why put it in MessageQueue We'll get it ?
MessageQueue Just get the next Message, And will Message return ,Looper Responsible for handling . This is conducive to the division of responsibilities , Consistent with a single division of responsibilities .
And when MessageQueue When data cannot be retrieved from , Need to get... Again , So we have to go into the dead circle , Until you get that Message.
The main problem is , The main thread cannot exit , Therefore, the main thread can be kept running by means of an endless loop .
② Here is the waiting state , If you are in a waiting state, why not wait()? While using native Of epoll Method ?
First wait, The monitor is used to control the thread state , It is a method to control multi-threaded access to shared resources , The current thread cannot be put into a waiting state .
Use native.epoll Method can put a thread into a waiting state .
③ If Message Of target == null, Then enter the cycle , Yes? target It can be for null Do you ? Not through Handler Is it set by default when sending messages ?
First of all ,target==null Is there anything special about the news of ? When target==null when , This message is a synchronization barrier message .
stay google Provided API in , We really can't send directly target Empty messages .
When the synchronization barrier message appears ,mq The asynchronous message behind the current barrier message will be read , Prioritize asynchronous messages , Until the synchronization barrier is removed .
⑥ Yes, it still needs time to judge , What about other messages that I have set delay ?
Why go straight to 6 了 ? Have a guess .
Where is the delayed message set ? What if the delay time I set is exceeded ?
In fact handler Send a message , When a message enters the queue , According to message The execution time of is sorted .
There's another problem , How to ensure that the message delay time is correct ? What if I change the system time ?
When Message Set up when Attribute , It's through SystemClock.updateMills() In order to get the , The execution time from the system startup to the present is obtained , So it will not change with the system time , So the time is accurate .
Handler stay Android Figure in the system
If you have read framework Source code of all know , stay Android There is a very important class in , It's called ActivityThead, In it there is such a H class . It is handler The subclass of contains the processing of many system messages .
class H extends Handler {
……
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
case RECEIVER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
handleReceiver((ReceiverData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CREATE_SERVICE:
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
("serviceCreate: " + String.valueOf(msg.obj)));
}
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNBIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
handleUnbindService((BindServiceData)msg.obj);
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
……
}
……
}
……
}
And when we call View.post When the method is used , It will eventually call Handler Of Post Method , So this Handler Where did it come from ?
In this Handler yes ViewRootImpl It's from . And in the ViewRootImpl There is also a custom Handler, This Handler Mainly used for what , You can discuss it by yourself . We found this Handler At the time of creation , There is no call Looper.prepare() Why is that ? Because what it gets is from the main thread Looper La .
final class ViewRootHandler extends Handler {
……
}
handler The ability boundary of
First of all, it is just a cross thread 、 Tools for process communication . So of course it can be used across threads 、 Cross process communication .
So through it Android Chinese identity , And all kinds of Handler The act performed , What else can be done ?
For example, a great God passed Handler The mechanism developed BlockCanary Such tools , To test code execution efficiency .
Some frameworks get the main thread Looper The realization of the main function 、 Sub thread switching .
Someone else has implemented the spring in the sub thread Toast.
Where is the boundary of its ability ? It can only be said , I don't know either ~~~
Other small Tips
Synchronization barrier
The question of synchronization barrier has been asked many times .
stay Handler There is a method to remove the synchronization barrier by sending .postSyncBarrier()、removeSyncBarrier()
After adding the synchronization barrier ,handler At the first barrier message (target==null The news of ) after , The asynchronous message behind the barrier message will be read first for priority execution .
Then again Android Where is it useful , After all, we can't pass this method directly api call .
stay View update ,draw、requestLayout、invalidate Wait a lot of places to call ViewRootImpl#scheduleTraversals()
because UI Update related messages are of the highest priority .
Handler How to do accurate timing ?
adopt SystemClock To get the time , This time is the running time from the start of the mobile phone to the present , It will not change with the system time .
View.post Why can we get the exact width and height , If it post Before drawing the event ?
Because the synchronization barrier gives priority to drawing code .
All right, all right , Today first BB So much , Wait to see more about the source code , Sum up more ~~
边栏推荐
- Create raspberry PI image file of raspberry pie
- Alvaria宣布客户体验行业资深人士Jeff Cotten担任新首席执行官
- In the wechat environment, H5 jumps to the specified page of the applet
- Sword finger offer 09 Implementing queues with two stacks
- 地理位置数据存储方案——Redis GEO
- Traversal and branch judgment of JS (case on June 24, 2022)
- Take you to the open source project of smart home: the preliminary configuration of zhiting home cloud and home assistant+ homebridge
- 说下你对方法区演变过程和内部结构的理解
- 什么是NFT数字藏品?
- 数据存储和传输文件之XML使用和解析详解
猜你喜欢

The style of the mall can also change a lot. DIY can learn about it!

Vscode有什么好用的插件?

异步处理容易出错的点
Prototype mode
Super comprehensive custom deep copy function

The style of the mall can also change a lot. DIY can learn about it!

VectorDraw Developer Framework 10.1001 Crack

VectorDraw Developer Framework 10.1001 Crack

《睡眠公式》:怎么治睡不好?

合宙Air32F103CBT6開發板上手報告
随机推荐
How to reload the win10 app store?
The style of the mall can also change a lot. DIY can learn about it!
Tensorflow loading cifar10 dataset
Introduction to MgO 256gb NAND flash chip
数据存储和传输文件之XML使用和解析详解
Rapport de la main - d'oeuvre du Conseil de développement de l'aecg air32f103cbt6
TFIDF与BM25
Most commonly used SQL statements
mysql整体架构和语句的执行流程
一文带你搞懂 JWT 常见概念 & 优缺点
GridLayout evenly allocate space
Sword finger offer 05 Replace spaces
商城风格也可以很多变,DIY了解一下!
Multithreading, parallelism, concurrency, thread safety
Don't underestimate the integral mall, its role can be great!
After the project is pushed to the remote warehouse, Baota webhook automatically publishes it
TensorFlow加载cifar10数据集
MySQL modifier l'instruction de champ
MySQL installation tutorial
JS中的==和===的区别(详解)