Android: What does posting a runnable from another thread onto the main thread actually do?

前端 未结 2 1198
-上瘾入骨i
-上瘾入骨i 2021-01-06 18:15

So, as the title says: What actually happens when you post a runnable from another thread onto the main thread?

I\'ve seen lots of questions asking how you do it, an

相关标签:
2条回答
  • 2021-01-06 18:29

    There are bunch of other runnables that the MainThread executes, such as updating the UI, touch events. The 'time' is when the posted runnable is ready to be dequeued. If any other runnable came before it, your runnable will wait.

    There is no such thing here as interruption. Your button will submit burst of runnables, as same as submitting same number of runnables from many different threads.

    If you have a message that is non-short (whatever contains LONG word is bad for UI) operation will block the execution of other recurrent tasks submitted in the queue, most of often demonstrated with no updates (for task that execution is indeterminate) on the UI at all or junking if it is matter of burst of runnables that execution takes longer than 8ms.

    0 讨论(0)
  • 2021-01-06 18:45

    First, you need to understand what the Message class is like. A Message object contains among other fields the following:

        Handler target;     // a handler that enqueued the message
        long when;          // the time at which the message is to be processed 
    
        [RUNNABLE] Runnable callback;   =
        [SWITCHED] int what, int arg1, int arg2, Bundle data...
    
        bool isAsynchronous; // I will talk about it in the end
    

    What I tagged with [RUNNABLE] and [SWITCHED] represents the two non-overlapping means of processing a Message. If the callback is not null all the [SWITCHED] fields are ignored. If the callback is null than the Message is defined by [SWITCHED] fields and is processed in either the Handler's overriden handleMessage() or the handleMessage() of the Handler.Callback the handler was initialized with.

    The MessageQueue is sorted by the when field. The Looper will not dequeue and process a message until the current time, as measured by SystemClock.uptimeMillis, is greater than or equal to the time stored in the message’s when field.

    When you call Handler#post(Runnable r) the following things happen:

    1. A Message is obtained from the pool (a simple static linked list in the Message class)

    2. Your Runnable is assigned to the message's callback field.

    3. when field is simply set to the current time if no delay or specific time was passed

    4. The Message is enqueued to the MessageQueue. If when is earlier than that of the head of the queue it becomes a new head. If it's not, than it's inserted in the middle so that the MessageQueue remains sorted by when

    5. The Looper which was in a non-terminating loop dequeuing the messages from the queue and processing them in sequence (no interweaving), eventually, dequeues our message and calls dispatchMessage() on the handler that originally posted the Runnable.

    6. The handler decides whether the message is [RUNNABLE] or [SWITCHED] and processes it accordingly. In particular it calls run() on the callback if it's present

    This should answer your questions on the behavior of your Runnable posted on the UI Thread during the blocking task - well, no, it does not interrupt the ongoing task, nor does it interweave. Everything that happens on the thread first gets into the MessageQueue, button clicks or your custom Runnables that you post from other threads. There is basically no way it could be happening some other way: Looper.loop() just makes the thread busy with its for(;;) loop.

    There are ways to change the messages ordering though.

    For instance, there is an interesting concept of sync-barrier in the Looper/Handler framework. A sync-barrier is by a convention just a Message with a null target (so it's basically just a flag-like thing, there is no handler to dispatch it). If it's put to the queue with postSyncBarrier(), the whole process of dequeuing changes, until the sync-barrier is removed from the queue with removeSyncBarrier(). The Messages not marked as isAsynchronous will be ignored and not dequeued and processed at all. Instead, the queue will be scanned until the message with isAsynchronous = true is found. It will then be scheduled according to its when and processed when its time comes.

    Also, you can call a self-explanatory Handler#postAtFrontOfQueue(), though, as pointed out in the documentation

    This method is only for use in very special circumstances -- it can easily starve the message queue, cause ordering problems, or have other unexpected side-effects.

    I suggest you browse the source code of all the classes mentioned. It reads like a good fiction book.

    0 讨论(0)
提交回复
热议问题