Android uses Handler to implement message delivery mechanism (2). androidhandler

Source: Internet
Author: User

Android uses Handler to implement message delivery mechanism (2). androidhandler

Before starting this article, let's summarize some key points about Handler, logoff, and MessageQueue in the first two articles:

0) before creating a Handler in the thread, you must first call logoff. prepare (), create a local thread variable logoff, and then call logoff. loop () to go to the round robin.

1) After Handler is created, you can call the sendMessageAtTime method of Handler to send messages. Instead, you can call the enqueueMessage method of MessageQueue to put the corresponding messages into the message queue.

2) Each thread has only one loue, which is responsible for round-robin of MessageQueue. When the Message is obtained, handler. dispatchMessage is called for distribution.

From the above three points, we can roughly see the Handler usage process.

Today, let's start from the beginning of the message, that is, the Handler's enqueueMessage method. The Code is as follows:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

This method mainly involves two tasks:

1ler set msg.tar get to the current Handler object

2) Call the enqueueMessage method of MessageQueue

Therefore, the Message object is put in the Message queue.

Speaking of MessageQueue, we must first understand that this message queue is actually a chain table structure, a string.

The initialization of its queue is not implemented at the java layer, but implemented at the JNI layer using C ++.

Let's take a look at the several native methods it defines, as follows:

    private native static long nativeInit();    private native static void nativeDestroy(long ptr);    private native static void nativePollOnce(long ptr, int timeoutMillis);    private native static void nativeWake(long ptr);    private native static boolean nativeIsIdling(long ptr);

The constructor is as follows:

    MessageQueue(boolean quitAllowed) {        mQuitAllowed = quitAllowed;        mPtr = nativeInit();    }

Here, we don't go into its JNI-Layer Code. The water is too deep.

Let's look at it from the Java layer. In the enqueueMesage method of MessageEnqueue, the main code is as follows:

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 {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                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;            }            ...        }


The above is the key code for entering the queue, and the operation is nothing more than inserting the message into the proper position in the queue according to the when field.

Since the message has been put into the queue, the next step is to get the message in the logoff round-robin operation and then distribute the message. We can see in the logoff loop method that it calls the next method of MessageQueue.

        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);

Obviously, the message is obtained in the next method of MessageQueue. Let's take a look at the method.

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;        }    }


The entire code is a bit long, but we can see the following points by removing some code that has little to do with implementation:

1) is a for (;) loop

2) The loop is exceeded only when the message is obtained or the mQuitting value is true.

3) when obtaining a Message, it will be determined based on the Message. when field.

From the above points, we can roughly understand why the next method may be blocked in the loop method, because it is an infinite round robin operation.

Okay, now we know the following two things:

1) Call the enqueueMessage method of MessageQueue in the sendMessageAtTime method of Handler and put the message into the queue.

2) call the next method of MessageQueue in the looop method of loop to retrieve messages from the queue.

In the next step, it is obvious that the dispatchMessage method of handler is called, as follows:

msg.target.dispatchMessage(msg);

At the beginning of the article, we found that msg.tar get is exactly the handler object, so the logic came to the dispatchMessage method of Handler, as follows:

    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

From the logic of the code, we need to know what the variable methods are:

1) msg. callback

2) mCallback

3) handleMessage

First, what is msg. callback?

In the Message class, we can see that

Runnable callback;

And its constructor:

    public static Message obtain(Handler h, Runnable callback) {        Message m = obtain();        m.target = h;        m.callback = callback;        return m;    }

It is actually a Runnable variable, which can be run in a new thread, and can be customized when the Message is obtained.

Therefore, in dispatchMessage, the first step is to determine whether a Runnable callback is set for the Message. If yes, execute the callback method as follows:

    private static void handleCallback(Message message) {        message.callback.run();    }

So what is the second mCallback? It is actually a built-in Handler interface, as follows:

    public interface Callback {        public boolean handleMessage(Message msg);    }

If our Handler implements this interface, this interface will give priority to message processing when messages are sent.

In general, only when we want to inherit the Handler class and implement a Custom Handler will we implement this interface. When this interface returns true, the default handleMessage method of Handler will no longer be called. Otherwise, it will still be called.

Finally, it is our most common handleMessage method, that is, the method we implement when implementing the most common handler.

Similarly, there are no examples. Please refer to the Code:

    class LooperThread extends Thread {        public Handler mHandler;        public void run() {            Looper.prepare();                mHandler = new Handler() {                                public void handleMessage(Message msg) {                    Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());                    switch (msg.what) {                    case MSG_ID_1:                        Log.v("Test", "Toast called from Handler.sendMessage()");                        break;                    case MSG_ID_2:                        String str = (String) msg.obj;                        Log.v("Test", str);                        break;                    }                }            };            Looper.loop();        }    }        protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());                LooperThread looperThread = new LooperThread();        looperThread.start();                       while(looperThread.mHandler == null){                                }                Message message = Message.obtain(looperThread.mHandler, new Runnable() {                        @Override            public void run() {                Log.v("Test", "Message.callack()");            }        });        message.what = MSG_ID_1;        message.sendToTarget();                looperThread.mHandler.post(new Runnable() {                        @Override            public void run() {                Log.v("Test", "Handler.callack()");            }        });    }

Here, we use the Message. obtain (Handler, Runnable) method and Handler. post method to construct and send messages. The result is as follows:

10-28 11:27:49.328: V/Test(22009): Id of MainThread : 110-28 11:27:49.328: V/Test(22009): Message.callack()10-28 11:27:49.328: V/Test(22009): Handler.callack()

Now, this article is over. I believe you should have a clear understanding of the entire Message flow process.



How to Use the Handler and message mechanisms of android to send messages to view pages

Handler handler = new Handler (){
Public void handleMessage (android. OS. Message msg ){
If (msg. what = 0 ){
// Jump code on the page .....
}
};
};

View event {
.....
Message msg = new Message ();
Msg. what = 0; (the value can be customized and matched with handlemessage)
Msg. obj = ""; // (the passed parameter, not included)
Handler. sendMessage (msg );
}

How to Use the Android Handler mechanism?

The Handler object is in the same thread as its caller. If a delayed operation is set in Handler, the calling thread will also be blocked. Each Handler object is bound to a logoff object, and each logoff object corresponds to a Message Queue (MessageQueue ). If the logoff object bound to the Handler is not specified when the Handler is created, The logoff object of the current thread is bound to the Handler by default.
In the main thread, you can directly use new Handler () to create a Handler object, which will be automatically bound to the logoff object of the main thread. If Handler is directly created in a non-main thread, an error will be reported, the Android system does not enable logoff in the main thread by default, and the Handler object must be bound to the logoff object. In this case, you must manually enable logoff in this thread. prepare () --> logoff. loop (), and then bind it to the Handler object; or use logoff. getMainLooper () to obtain the Looper of the main thread and bind it to this Handler object.
All the messages sent by Handler are added to the logoff MessageQueue. Handler contains two queues: thread queue and message queue. post () can add the thread object to the thread queue; Use Handler. sendMessage () can be used to add a message object to a message queue. The source code analysis shows that Handler has only one message queue, namely, MessageQueue. The thread object passed through post () will be encapsulated into a message object and passed into MessageQueue.
After a thread object is put into a Message Queue using post (), when logoff polls the thread for execution, it does not actually start a new thread separately, but it is still executed in the thread bound to the current logoff. Handler only calls the run () of the thread object. For example, the UI update command is defined in the Child thread. If the thread is directly enabled for execution, an error is reported () add it to the Logoff of the main thread and execute it to update the UI.
After you use sendMessage () to add a message object to a message queue, when logoff polls the message, handleMessage () of Handler is called to process it. Taking UI update as an example, if this method is used, the Logoff of the main thread is first bound to the Handler object, and handleMessage () is overloaded to process UI updates, then you can send a message to it.

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.