当前位置:网站首页>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

  1. You know Android Of Handler Mechanism ?( I said with a smile , That must be understood , After a colloquial conversation, we have the following questions )
  2. You know, Handler On Android framework Are there any applications in ?
  3. You know, Handler Synchronization barrier in ?
  4. Why are you passing view.post() Acquired view Width and height are accurate ?post Won't the message appear after the measurement process ?
  5. 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

  1. Android Message processing system in
  2. Handler How it turns
  3. Handler stay Android Figure in the system
  4. 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

  1. The sender packs the goods , Give the package to the express company .
  2. The express company loads the packages that many people want to send to the target city .
  3. 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 :

  1. handler It is equivalent to a courier . Be responsible for receiving 、 Express delivery .
  2. Message It's a courier . Of course, express delivery is also divided into ( Express delivery 、 Ordinary express delivery 、 Empty bag )
  3. MessageQueue It is a warehouse for express delivery
  4. Looper It is used to distribute each express to the corresponding courier , It can be used as an express company .
  5. The sender is the thread A
  6. 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 .

 Insert picture description here

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 :

  1. establish Looper,Looper.prepare().
  2. establish Handler, Used for sending and receiving messages .
  3. 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 ~~

原网站

版权声明
本文为[Favorite grapes]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202190538056773.html