Using handler to implement message distribution mechanism in Android (ii)

Source: Internet
Author: User

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

0) before creating handler in thread, you must first call Looper.prepare (), create a thread local variable looper, and then call Looper.loop () to go round.

1) When handler is created, it is possible to call handler's Sendmessageattime method to send a message, instead of invoking the Enqueuemessage method of MessageQueue and putting the corresponding message into the message queue.

2) Each thread has only one looper, and this looper is responsible for MessageQueue rotation, and when it gets to the message, it calls Handler.dispatchmessage for distribution.

From the above three points, we can probably see the use of handler flow.

Today we start from the beginning of the story, is handler 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 does two things:

1) Set Msg.target as the current handler object

2) Call the Enqueuemessage method of MessageQueue

So, in fact, this is where you put the message object in the messages queue.

Speaking of MessageQueue, we must first understand that this message queue is actually a list of the structure, a string of one.

and its queue initialization is not done in the Java layer, but in the JNI layer using C + + implementation.

We can look at several native methods of their definition, 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);

And its constructor is as follows:

    MessageQueue (Boolean quitallowed) {        mquitallowed = quitallowed;        Mptr = Nativeinit ();    }

Here, we do not enter its code in the JNI layer, the water is too deep.

Let's take a look at the Java layer. In the Messageenqueue Enqueuemesage method, 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 are a barrier at the head of the the queue                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; }            ...        }


Above is the key code into the queue, and what it does is to insert the message into the appropriate position in the queue based on the When field.

Now that the message is in the queue, the next step is to get the message in Looper's round-robin operation, and then distribute the message. We can see in the Looper 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 is in a local variable with 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);

So obviously, in the next method of MessageQueue, get the message, let's go into its method to see it.

Message Next () {int pendingidlehandlercount =-1;//-1 only during first iteration int Nextpolltimeoutmill        is = 0; for (;;)            {if (Nextpolltimeoutmillis! = 0) {binder.flushpendingcommands ();            }//We can assume mptr! = 0 because the loop is obviously still running.            The looper would not be the call of 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 was 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, all pending messages has 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 being 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 (PE NdingiDlehandlercount, 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 don't run them            Again.       Pendingidlehandlercount = 0;     While calling an idle handler, a new message could has been delivered//so go back and look again for            A pending message without waiting.        Nextpolltimeoutmillis = 0; }    }


The whole code is a little bit long, but we're going to get rid of some code that doesn't really matter to us, and we can see the main points:

1) is a for (;;) Cycle

2) jumps out of the loop only when the message is acquired or when Mquitting is true.

3) When the message is obtained, it is judged according to the Message.when field.

From the above, we can probably understand why the next method in the loop method is likely to block because it is an infinite round-robin operation.

Well, when we get here, we probably know the following two things:

1) in the handler Sendmessageattime method, call MessageQueue's Enqueuemessage method and put the message into the queue.

2) in Looper's Looop method, call MessageQueue's next method to take the message out of the queue.

The next third step, obviously, is to call handler's DispatchMessage method, as follows:

Msg.target.dispatchMessage (msg);

At the beginning of the article, we noticed that Msg.target is just the handler object, so logic came to handler DispatchMessage method, 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 understand what a few variable methods are all about:

1) Msg.callback

2) Mcallback

3) Handlemessage

First of all, what is Msg.callback?

In the message class, we can see

Runnable callback;

There are also its constructors:

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

is actually a runnable variable that can be put into a new thread to run, and can be customized when you get the message.

So in DispatchMessage, the first is to determine whether there is a runnable callback to the message, if so, execute this callback method, as follows:

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

So, what is the second mcallback, which is actually a handler built-in interface, as follows:

    Public interface Callback {public        boolean handlemessage (Message msg);    

If there is a handler to implement this interface, then when the message is distributed, the interface will first process the message.

In general, only if we want to inherit the handler class and implement the custom handler, we will implement this interface, and when this interface returns TRUE, the handler default Handlemessage method is no longer called. Conversely, it will still be called.

In the end, it is our most common method of Handlemessage, which is the method we implement when we implement the most common handler.

Similarly, there is no example of how it can be, see 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.send                        Message () ");                    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 (loo Perthread.mhandler, New Runnable () {@Override public void run () {LOG.V (            "Test", "Message.callack ()");        }        });        Message.what = msg_id_1;                LooperThread.mHandler.sendMessage (message);                LooperThread.mHandler.post (New Runnable () {@Override public void run () {            LOG.V ("Test", "Handler.callack ()");    }        }); }

Here, we use the Message.obtain (Handler, Runnable) method and the Handler.post method to construct and send the message, resulting in the following results:

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 ()

Well, this article to the end, I believe that the whole message of the flow of the process, should have a clear understanding of it.


Using handler to implement message distribution mechanism in Android (ii)

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.