Android message mechanism Exploration (handler,looper,message,messagequeue)

Source: Internet
Author: User

Overview

The Android messaging mechanism is an important piece of the Android operating system. The specific use of the method is not described here, you can refer to the official Android development documentation.

There are two main uses of the messaging mechanism:

1, the communication between the threads . For example, in a child thread that wants to update the UI, it is implemented by sending an update message to the UI thread.

2. delay the execution of the task. For example, after 30 seconds to perform a refresh task.

The message mechanism runs roughly as follows:

There can be only one Looper object in a thread, one Looper object holds a message queue, one message queue maintains multiple message objects, and one looper can create multiple handler objects, handler objects are used to send messages to the message queue, and processing the messages that Looper distributes to itself (that is, those messages that you send earlier), these handler objects can run across threads, but the final message is processed in the thread that created the handler.

Analysis

After familiar with the basic usage, it is necessary to explore in depth.

Logical Analysis

The framework layer of the Android messaging mechanism revolves around the four objects handler, Looper, message, MessageQueue. The message mechanism is mainly the operation of generating, sending, storing, distributing and processing the message.

Message:

This class represents the message object in the message mechanism. Is the object that is created in the message mechanism to pass data and operations, and is responsible for maintaining the message object cache pool.

The message object mainly has the following properties:

What: Message type.

Arg1, arg2, obj, data: The information field of the message

When: the time at which the message should be processed, and the value of the field is Systemclock.uptimemillis (). When the value of the field is 0 o'clock, the message needs to be placed in the header of the message queue.

Target: The handler object that sends and processes the message.

Next: The next message of the message in the object pool.

Message object, a pool of message objects is maintained primarily, because the message object is frequently used in the system, so the expense of frequently creating objects is reduced by the way the object pool is used. The Message object pool is implemented using a single-linked list. The maximum number limit is 50. So the official recommendation is that we get the message object through the object pool.

It is particularly important to note that we usually use a common message object, which is the synchronous message object. There are actually two special message objects that are rarely used at the moment, but it is also necessary to understand them.

The first is the Barrier message of the synchronization, which means that if the message arrives at the header of the message queue, the other synchronization messages in the message queue are blocked and cannot be processed. The feature of the barrier message is Target==null&&arg1==barriertoken

The second is an asynchronous message, and the asynchronous message is not affected by the above-mentioned barrier message. Sets a message to an asynchronous message by using the Setasynchronous (Boolean async) method of the Message object.

MessageQueue:

This class represents the message queue in the message mechanism. It is mainly to maintain a thread-safe Message Queuing, to provide the queue of messages, delete, as well as the blocking mode of polling out and other operations.

Looper:

This class represents the message dispatcher in the message mechanism. With the message, Message Queuing, and the lack of an object to process the message distribution mechanism, Looper is the object that handles the message distribution mechanism. It will send each message to the correct processing object for processing. If a looper starts working and there is no message processing, the thread will be blocked. In a non-UI thread, this time the idle event of the current MessageQueue should be monitored, and if there is currently an idle event, you should exit the current message loop and then end the thread and release the appropriate resources.

Handler:

This class represents the message sending and the processor in the message mechanism. With the message, message queue, message distribution mechanism, message delivery and message processing are also missing. Handler is used to do message delivery and message processing. Handler event processing mechanism uses a priority level of freedom from high to low for message processing, under normal circumstances, a handler object can set a callback property, a handler object can manipulate multiple message objects, in a way, Creating a Message object is more freedom than setting the callback property to a handler object, and setting the callback property to a handler object is more free than deriving a handler subclass, so the message processing priority is message> Handler.callback.Handler.handleMessage ().

Code Analysis

Important part of the source code parsing, based on the SDK.

Message:

A generic Message object that contains the message type, data, and behavior. The internal contains an object pool implemented with a single-linked list, with a maximum number of 50, to avoid the overhead of frequent object creation.

1. Gets the message object from the object pool.

Source:

public static Message obtain () {        synchronized (spoolsync) {            if (sPool! = null) {                Message m = sPool;                SPool = M.next;                M.next = null;                m.flags = 0; Clear In-use flag                spoolsize--;                return m;            }        }        return new Message ();    



Pseudo code:

public static Message obtain () {        synchronized (spoolsync) {            if (object pool is not empty) {                An object is fetched from the pool of objects implemented by the single-linked list (obtained from the linked list header);        Clears the object's flag bit (in use, async, etc.);                Fixed object pool size;                Return the Message object to remove;}        }        return new Message object;    }

2. Return the Message object to the object pool

Source code:

void recycleunchecked () {        //Mark the message as in use while it remains in the recycled object pool.        Clear all other details.        Flags = Flag_in_use;        what = 0;        arg1 = 0;        arg2 = 0;        obj = null;        ReplyTo = null;        Sendinguid =-1;        when = 0;        target = null;        callback = NULL;        data = null;        Synchronized (spoolsync) {            if (Spoolsize < max_pool_size) {                next = SPool;                SPool = this;                spoolsize++;}}}    



Pseudo code:

void recycleunchecked () {        flag is in use;        Clears other data from the current object;        Synchronized (spoolsync) {            if (the object pool has no capacity to reach the upper limit) {                Inserts the object in the single-linked list header;                Fixed object pool size;}}}    

Looper:

Using threadlocal to implement thread-scoped control, each thread has at most one Looper object, and the internal holds a MessageQueue reference.

1. Initialize a looper

Source:

    private static void Prepare (Boolean quitallowed) {        if (sthreadlocal.get () = null) {            throw new RuntimeException (" Only one Looper could be created per thread ");        Sthreadlocal.set (New Looper (quitallowed));    }


Pseudo code:

    private static void Prepare (Boolean quitallowed) {        if (a Looper object already in the current thread) {            throw new RuntimeException (" A thread can only create one Looper object ");        }        Re-instantiate a looper that can be exited;        Associate the Looper object with the current thread;    }

2, Looper began to work

Source:

    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 the the the the The local process,//and keep track of what the identity toke        n actually is.        Binder.clearcallingidentity ();        Final Long ident = Binder.clearcallingidentity (); for (;;) {Message msg = Queue.next ();//might block if (msg = = NULL) {//No Message Indicat                ES the message queue is quitting.            Return }//This must is in a local variable, in case a UI event sets the logger Printer logging = Me.mlogg            ing                        if (logging! = null) {logging.println (">>>>> dispatching to" + Msg.target + "" + Msg.callback + ":" + MSG.WHAT);            } msg.target.dispatchMessage (msg); if (logging! = null) {logging.println ("<<<<< finished to" + Msg.target + "" + Msg.callbac            k);             }//Make sure that during the course of dispatching the//identity of the thread wasn ' t corrupted.            Final Long newident = Binder.clearcallingidentity (); if (ident! = newident) {LOG.WTF (TAG, "Thread identity changed from 0x" + Long.tohex                        String (ident) + "to 0x" + long.tohexstring (newident) + "when dispatching to"            + Msg.target.getClass (). GetName () + "" + Msg.callback + "what=" + msg.what);        } msg.recycleunchecked (); }    }

Pseudo code:

   public static void Loop () {        Gets the Looper object for the current thread;        if (the current thread does not have a Looper object) {            throw new RuntimeException ("No looper; Looper.prepare () has not been called in the current thread ");        }        Gets the MessageQueue associated with the Looper object;        Removal of IPC identity marks;        for (;;) {            A message is obtained from MessageQueue, and if the current MessageQueue does not have a message, it is blocked;            If (no message is taken) {                //no message means Message Queuing exited.                return;            }            print logs;                        Invokes the target of the current message object to process the message, that is, the handler object that sent the message;            print logs;            Obtaining a new IPC identification;            if (IPC ID changed) {                print warning message;            Reclaims the message and puts it into the Message object pool;}    }


Handler:

Responsible for the delivery of messages, scheduled send, delay sending, message processing and other actions.

1. Event handling

Source:

public void DispatchMessage (Message msg) {        if (msg.callback! = null) {            handlecallback (msg);        } else {            if ( Mcallback! = null) {                if (Mcallback.handlemessage (msg)) {                    return;                }            }            Handlemessage (msg);        }    }


Pseudo code:

public void DispatchMessage (message msg) {        if (the message's callback property is not empty) {The            run () method of the callback object that runs the message;        } else { C16/>if (The Mcallback property of the current handler object is not empty) {                if (the Mcallback object successfully processed the message) {                    return;                }            }            Handler internal processing of the message;        }    }

MessageQueue:

A single-linked list maintains a message queue, increasing the performance of operations such as frequent insertion of delete messages, which are sorted by the When field of the message, and the first processed message is queued at the front of the list. Internal blocking polling and wakeup operations are implemented using JNI.

1. Queued operation of Message object

Source:

Boolean enqueuemessage (Message msg, long when) {if (Msg.target = = null) {throw new Illegalargumentexce        Ption ("Message must has a target.");        } if (Msg.isinuse ()) {throw new IllegalStateException (msg + "This message is already"); } synchronized (this) {if (mquitting) {illegalstateexception e = new Illegalstateexce                Ption (Msg.target + "sending message to a Handler on a dead thread");                LOG.W (TAG, E.getmessage (), E);                Msg.recycle ();            return false;            } msg.markinuse ();            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;            }//We can assume mptr! = 0 because mquitting is false.            if (needwake) {nativewake (mptr);    }} return true; }


Pseudo code:

The Boolean enqueuemessage (Message msg, long when) {if (the message does not have a target) {throw new IllegalArgumentException ("        The message object must have a target ");        If (the message is being used) {throw new IllegalStateException (msg + "The message is in use");                } synchronized (this) {if (Message queue exited) {print warning message;                Reclaims the message and returns it to the Message object pool;            return false;            } set the message to be in use;            Sets the time the message will be processed;            Message p = mmessages;            Boolean needwake;                if (msg queue is empty | | The Message object request is placed in the team header | | Execution time precedes the execution time of the current team first MSG (all delay msg in the current queue) {Adds the current MSG to the MSG queue header;            If it is blocked, set to need to be awakened;                } else {if (blocked && first is barrier && current MSG is asynchronous msg) {Set to need to be awakened } for (;;)                    {According to Msg.when, find the appropriate insertion position, first executed in front of the queue;                    If (there is an asynchronous message before waking && inserting position) {No Wake required;                }} insert into the appropriate position;            } if (requires wake-up) {call the native method to wake locally;    }} return true; }

2. Query Pending messages

Source:

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 are not supported.        Final long ptr = mptr;        if (ptr = = 0) {return null; } int pendingidlehandlercount =-1;        -1 only during first iteration int nextpolltimeoutmillis = 0; for (;;)            {if (Nextpolltimeoutmillis! = 0) {binder.flushpendingcommands ();            } 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 (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 (DEBUG) log.v (TAG, "returning message:" + msg);                        Msg.markinuse ();                    return msg; }} else{//No more messages.                Nextpolltimeoutmillis =-1;                }//Process the Quit message now and 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 (pendingidlehandler                Count, 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 (TAG, "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 PE            Nding message without waiting.        Nextpolltimeoutmillis = 0; }    }

Pseudo code:

Message Next () {if (Message queue exited) {return null;        } mark the number of idle events as the first time;        The waiting (blocking) time for the next poll is set to 0; for (;;)            {if (next poll requires blocking) {Clear binder pending command, used to release resources;            Use the current settings to poll for blocking time to do a native poll, if the blocking time is greater than 0, it will block until the message is taken;                Synchronized (this) {if (Message queue header is barrier message) {The first asynchronous message is fetched; if (query to the message that satisfies the condition) {if (not yet execution time of the message) {Set the next polling blocking time to Msg.when-no                    W, maximum no more than integer.max_value;                        } else {The blocking identity is set to false;                        Remove the message, redirect the chain header;                        Mark the message as in use;                    return the message;                }} else {No message, setting the next polling block time is-1, not blocking;                    } if (Message queue exited) {resources are freed;                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 (first idle event) {Calculates the number of idle listeners;                    } if (no idle listener) {Blocking identity set to true;                Continue            } Generate idle Listener object;            } for (int i = 0; i < Pendingidlehandlercount; i++) {Notifies the listener of the idle event, depending on the identity to determine whether the listener continues to listen.            } Set the idle event identity as not the first time;        After the idle listener is called, there may be new messages entering the queue, so the next polling block time is set to 0; }    }

Android message mechanism Exploration (handler,looper,message,messagequeue)

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.