From the source code analysis Handler mechanism, the source code handler Mechanism

Source: Internet
Author: User

From the source code analysis Handler mechanism, the source code handler Mechanism

In Android, to update the ui, We must update the ui in the main thread. When the main thread is blocked for more than 5 seconds, an anr exception occurs, will cause program crash. Therefore, some time-consuming operations must be placed in the Child thread, but the ui update operations cannot be performed in the Child thread. To solve this problem, Android has designed the handler mechanism, the appearance of handler sets up a communication bridge between the main thread and the sub-process, which improves the ui update problem. The following describes handler. ActivityThread starts the main thread of the application. In the main method of ActivityThread:

public static final void main(String[] args) {        SamplingProfilerIntegration.start();        Process.setArgV0("<pre-initialized>");        Looper.prepareMainLooper();        if (sMainThreadHandler == null) {            sMainThreadHandler = new Handler();        }        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();       ......}

From the code above, we can see that we must first execute the logoff. preparemainlogoff (); operation, and then enter the loop for loop. In preparemainloue, call the prepare method to use sThreadLocal to set a Looper for the current thread. If the current thread does not exist, initialize a Looper and create a MessageQueue In the Looper constructor. Careful readers may notice that both the preparemainlogoff and prepare methods are static, and sThreadLocal is also a static variable. First of all, the existence of the sub-thread is not considered, and only the main thread is considered, so no matter where the application is, we call logoff. preparemainlogoff (); Through sThreadLocal. get () gets all the same logoff object. This ensures that there is only one logoff object in a thread, which means that there is only one MessageQueue in a thread.

public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        } }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)); }private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();}

After executing lorule. preparemainlorule (), it starts lorule. loop () to read and distribute messages cyclically. This will be analyzed after Handler is analyzed.

Next we will analyze Handler.
We often use this in code:

private Handler handler = new Handler(){    public void handleMessage(Message msg) {        // process incoming messages here    }}public Handler(Callback callback, boolean async) {    ......    mLooper = Looper.myLooper();    if (mLooper == null) {        throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()");    }    mQueue = mLooper.mQueue;    mCallback = callback;    mAsynchronous = async;}

In the Handler constructor. mylogoff () obtains the unique logoff object in the thread and initializes the message queue in the hanlder. This message queue is the same as the first initialized message queue in the logoff.
When the handler. sendMessage or sendEmpty method is called, the final method to be followed is the sendMessageAtTime method:

 public boolean sendMessageAtTime(Message msg, long     uptimeMillis) {    MessageQueue queue = mQueue;    if (queue == null) {        RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");        Log.w("Looper", e.getMessage(), e);        return false;    }    return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(MessageQueue queue, Message msg,     long uptimeMillis) {    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    return queue.enqueueMessage(msg, uptimeMillis);}

In the enqueueMessage method, set the target to this for the msg to be added to the Message Queue. this is the current handler object, mainly for the following logoff to loop out the message, it is convenient to know where the msg is distributed and which handler is responsible for processing it. Then, call the enqueueMessage method of MessageQueue to add msg to the queue.

boolean enqueueMessage(Message msg, long when) {    ......    synchronized (this) {        ......        msg.when = when;        Message p = mMessages;        boolean needWake;        if (p == null || when == 0 || when < p.when) {            // New head, wake up the event queue if blocked.            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            needWake = mBlocked && p.target == null &&  msg.isAsynchronous();            Message prev;            for (;;) {                prev = p;                p = p.next;                if (p == null || when < p.when) {                    break;                }                if (needWake && p.isAsynchronous()) {                    needWake = false;                }            }            msg.next = p; // invariant: p == prev.next            prev.next = msg;        }        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

When the message does not have any msg in the column, the currently added msg should be the queue header, and we can see from the else statement that, the entire message pair column is a circular pair column. At this time, the message has msg in the column, so the msg should be accepted and distributed, and The logoff. loop () method is called in ActivityThread for message polling.

public static void loop() {    final Looper me = myLooper();    if (me == null) {        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");    }    final MessageQueue queue = me.mQueue;    // Make sure the identity of this thread is that of the local process,    // and keep track of what that identity token actually is.    Binder.clearCallingIdentity();    final long ident = Binder.clearCallingIdentity();    for (;;) {        Message msg = queue.next(); // might block        if (msg == null) {            return;        }        Printer logging = me.mLogging;        .......        msg.target.dispatchMessage(msg);        final long newIdent = Binder.clearCallingIdentity();        if (ident != newIdent) {        }        msg.recycle();    }}

Here, the for loop is an endless loop, and some may wonder, logoff. loop () is running in the main thread, but is an endless loop. Isn't it true that a timeout operation cannot be performed in a good main thread? Haha, because when polling messages in the message queue in a loop, if there is no message, it will be blocked. So you don't have to worry about anr. Pull get is the handle to process the message, which is why the dispatchMessage method needs to be rewritten in handler. Finally, call recycle to release the Message. The reason for this is that the Message can be either in the new or Message mode. the obtain method obtains one message from the message pool because the number of messages in the message pool is limited. If the recycle is not timely after the message is used up, the msg object cannot be reused.

Next, analyze the queue. next () method in detail. We can see in the comment that this method may be blocked because there is no message in the message queue.

Message next() {    int pendingIdleHandlerCount = -1; // -1 only during first iteration    int nextPollTimeoutMillis = 0;    for (;;) {        if (nextPollTimeoutMillis != 0) {            Binder.flushPendingCommands();        }        // We can assume mPtr != 0 because the loop is obviously still running.        // The looper will not call this method after the loop quits.        nativePollOnce(mPtr, nextPollTimeoutMillis);        synchronized (this) {            // Try to retrieve the next message.  Return if found.            final long now = SystemClock.uptimeMillis();            Message prevMsg = null;            Message msg = mMessages;            if (msg != null && msg.target == null) {                // Stalled by a barrier.  Find the next asynchronous message in the queue.                do {                    prevMsg = msg;                    msg = msg.next;                } while (msg != null && !msg.isAsynchronous());            }            if (msg != null) {                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;                    if (prevMsg != null) {                        prevMsg.next = msg.next;                    } else {                        mMessages = msg.next;                    }                    msg.next = null;                    if (false) Log.v("MessageQueue", "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 (mQuitting) {                dispose();                return null;            }            // If first time idle, then get the number of idlers to run.            // Idle handles only run if the queue is empty or if the first message            // in the queue (possibly a barrier) is due to be handled in the future.            if (pendingIdleHandlerCount < 0                    && (mMessages == null || now < mMessages.when)) {                pendingIdleHandlerCount = mIdleHandlers.size();            }            if (pendingIdleHandlerCount <= 0) {                // No idle handlers to run.  Loop and wait some more.                mBlocked = true;                continue;            }            if (mPendingIdleHandlers == null) {                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];            }            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);        }        // Run the idle handlers.        // We only ever reach this code block during the first iteration.        for (int i = 0; i < pendingIdleHandlerCount; i++) {            final IdleHandler idler = mPendingIdleHandlers[i];            mPendingIdleHandlers[i] = null; // release the reference to the handler            boolean keep = false;            try {                keep = idler.queueIdle();            } catch (Throwable t) {                Log.wtf("MessageQueue", "IdleHandler threw exception", t);            }            if (!keep) {                synchronized (this) {                    mIdleHandlers.remove(idler);                }            }        }        // Reset the idle handler count to 0 so we do not run them again.        pendingIdleHandlerCount = 0;        // While calling an idle handler, a new message could have been delivered        // so go back and look again for a pending message without waiting.        nextPollTimeoutMillis = 0;    }}

NativePollOnce (mPtr, nextPollTimeoutMillis) blocks when no message is returned during polling. The message wake-up and blocking mechanisms will be introduced in the next article.

Finally, I will summarize Handler. From Message distribution to Message processing, the terms logoff, MessageQueue, Thread, Handler, and Message have been used successively.

  • Logoff: initializes the message queue and continuously polls messages from the message queue.
  • MessageQueue: A Message Queue that is stored in messages sent by handler.
  • Thread: the location or running environment operated by the current message queue and logoff.
  • Handler: responsible for sending and processing messages.
  • Message: a Message that updates the UI, which is sent by handler and lined up by MessageQueue.

The message processing mechanism involves the following process:

For more information about the blocking and wakeup mechanisms during polling, see the next article.

Supplement: you can update the ui in the Child thread in this way.

    class LooperThread extends Thread {        public Handler mHandler;       public void run() {           Looper.prepare();            mHandler = new Handler() {                public void handleMessage(Message msg) {                   // process incoming messages here                }            };            Looper.loop();        }    }

Copyright statement: This article is the original author of the blog, without the master can not be reproduced (contact: QQ312037487 mailbox: andywuchuanlong@sina.cn ).

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.