Android basics 02 -- thread security 3: Message, messagequeue, handler, Logoff

Source: Internet
Author: User

Android UI operations are not thread-safe, and only the main thread can operate the UI. At the same time, the main thread has a certain time limit on UI operations (up to 5 seconds ). To perform some time-consuming operations (such as downloading and opening large files), Android provides some column mechanisms. The articles in the "android basics 02-thread security" series refer to a series of articles compiled by many online users and introduce the main methods. They are as follows:

Android basics 02 -- thread security 1: Definitions and Examples

Android basics 02 -- thread security 2: handler, message, and runnable

Android basics 02 -- thread security 3: Message, messagequeue, handler, Logoff

Android basics 02 -- thread security 4: handlerthread

Android basics 02 -- thread security 5: asynctask

In the above example, when a message queue is used, we have to mention the related message, messagequeue, handler, and logoff.

The relationships between threads, messagequeue, handler, and logoff can be shown in a diagram:

1. Handler

-- Used to process messages and threads

-- Each handler can execute a thread object (runnable implementation) and send and process messages.
> Post (runnable)
> Postattime (runnable, long)
> Postdelayed (runnable, long)
> Sendemptymessage (INT)
> Sendmessageattime (message, long)
> Sendmessagedelayed (message, long)
> Handlemessage (Message)
With handler, you can publish or process a message or a runnable instance. Each handler is managed with a unique thread and the message queue of the thread. When you create a new handler, by default, it will be associated with the thread that created it and the message queue of the thread. That is to say, if you publish a message through handler, the message will only be sent to the Message Queue associated with it, and of course only the messages in the Message Queue can be processed.
The main methods are as follows:
1) public final Boolean sendmessage (Message MSG)
Put the message into the Message Queue associated with the handler and put it after all the messages not processed before the current time.
2) Public void handlemessage (Message MSG)
The Thread associated with the message queue receives and processes messages by calling the handlemessage method of handler. handlemessage is usually implemented by subclass handler.
Removemessages (0) to clear the message queue.

This is an introduction I found on the network. If you see the source code of handler, it will introduce it as follows:

* A handler allows you to send and process {@ link message} and runnable
* Objects associated with a thread's {@ link messagequeue}. Each Handler
* Instance is associated with a single thread and that thread's message
* Queue. When you create a new handler, it is bound to the thread/
* Message queue of the thread that is creating it -- from that point on,
* It will deliver messages and runnables to that message queue and execute
* Them as they come out of the message queue.
*
* <P> there are two main uses for a handler: (1) to schedule messages and
* Runnables to be executed as some point in the future; and (2) to enqueue
* An action to be passed med on a different thread than your own.

public class Handler {    private static final boolean FIND_POTENTIAL_LEAKS = false;    private static final String TAG = "Handler";    public interface Callback {        public boolean handleMessage(Message msg);    }    final MessageQueue mQueue;    final Looper mLooper;    final Callback mCallback;    IMessenger mMessenger;    /**     * Subclasses must implement this to receive messages.     */    public void handleMessage(Message msg) {    }    public Handler() {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }    }        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 = null;    }    /**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }    public Handler() {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }        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 = null;}public boolean sendMessageAtTime(Message msg, long uptimeMillis)    {        boolean sent = false;        MessageQueue queue = mQueue;        if (queue != null) {            msg.target = this;            sent = queue.enqueueMessage(msg, uptimeMillis);        }        else {            RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);        }        return sent;    }

From the source code analysis, we can see that:
1) handler calls logoff in the construction method without parameters. the mylogoff () method obtains a logoff object from the current thread. If not, it is created. in this way, the logoff is initialized, And the messagequeue is initialized at the same time, and the messagequeue of logoff is obtained. it can be seen that handler is the manager and dispatcher of logoff and messagequeue.

2) The most important method is sendmessageattime (Message MSG, long uptimemillis). When you send a message to handler, the Code shows that he does not process the message himself, instead, it is handed over to messagequeue. the following code is used to handle the problem:

3) when processing a runnable object, it creates a new message and sets its mcallback to the runnable object, and inserts the message into messagequeue.

4) handler functions include processing messages (executing message content) and forwarding messages, which are mainly processed by handlemessage and dispatchmessage functions;

5) in handler, both send message and post runnable are added to messagequeue.

Queue. enqueuemessage (MSG, uptimemillis). The specific implementation depends on the following.

2. Message

Frameworks \ base \ core \ Java \ Android \ OS \ message. Java

-- Defines an information object, which can be sent to the handler object for processing.
-- It can be instantiated through message. Obtain () or handler. obtainmessage ().
Message is the carrier of information transmitted between threads. It contains the Message description and any data objects. Message contains two additional int fields and an object field. In most cases, you do not need to allocate memory. Although the message constructor is public, it is best to use message. obtain () or handler. the obtainmessage () function is used to obtain the message object, because the implementation of the message contains a mechanism for recycling and reuse, which can provide efficiency.

* Defines a message containing a description and arbitrary data object that can be
* Sent to a {@ link handler}. This object contains two extra int fields and
* Extra object field that allow you to not do allocations in your cases.

public final class Message implements Parcelable {    public int what;    public int arg1;    public int arg2;    public Object obj;    public Messenger replyTo;    long when;    Bundle data;    Handler target;         Runnable callback;     Message next;    private static Object mPoolSync = new Object();    private static Message mPool;    private static int mPoolSize = 0;    private static final int MAX_POOL_SIZE = 10;

When: the time when a message is generated to handler.
Data: bind the data to be passed in the thread on the bundler object
Next: reference of the current message
Handler: Handler object for processing the current message.
Mpool: a message pool may be called literally, but it is more suitable to call a message chain referenced by the next message through analysis.
Here, message. Obtain () is used to obtain the first message that breaks the message link through source code analysis.

The source code can be interpreted as follows:

1) message. Obtain () reads a message from the global message pool, and puts the message into the pool when it is recycled.

2) The parcelable interface is implemented in message.

3. messagequeue

-- Maintain a message list. messages in this list are distributed by logoff objects.
-- Call logoff. myqueue () to obtain a messagequeue object
Message Queue is a message queue used to store messages published through handler. A message queue is usually attached to a thread that creates it. You can use logoff. myqueue () to obtain the message queue of the current thread. Android will create an associated message queue for the UI thread by default at the first startup, which is used to manage some upper-layer components of the program, such as activities and broadcast receivers. You can create handler and UI thread communication in your child thread.

/**

* Low-level class holding the list of messages to be dispatched by
* {@ Link logoff}. messages are not added directly to a messagequeue,
* But rather through {@ link handler} objects associated with the looper.
* <P> you can retrieve the messagequeue for the current thread
* {@ Link loue # myqueue () loue. myqueue ()}.
*/

public class MessageQueue {    Message mMessages;    private final ArrayList mIdleHandlers = new ArrayList();    private boolean mQuiting = false;    boolean mQuitAllowed = true;        public static interface IdleHandler {        boolean queueIdle();}public final void addIdleHandler(IdleHandler handler) {     if (handler == null) {         throw new NullPointerException("Can't add a null IdleHandler");     }        synchronized (this) {            mIdleHandlers.add(handler);        }}final boolean enqueueMessage(Message msg, long when) {        if (msg.when != 0) {            throw new AndroidRuntimeException(msg                    + " This message is already in use.");        }        if (msg.target == null && !mQuitAllowed) {            throw new RuntimeException("Main thread not allowed to quit");        }        synchronized (this) {            if (mQuiting) {                RuntimeException e = new RuntimeException(                    msg.target + " sending message to a Handler on a dead thread");                Log.w("MessageQueue", e.getMessage(), e);                return false;            } else if (msg.target == null) {                mQuiting = true;            }            msg.when = when;            //Log.d("MessageQueue", "Enqueing: " + msg);            Message p = mMessages;            if (p == null || when == 0 || when < p.when) {                msg.next = p;                mMessages = msg;                this.notify();            } else {                Message prev = null;                while (p != null && p.when <= when) {                    prev = p;                    p = p.next;                }                msg.next = prev.next;                prev.next = msg;                this.notify();            }        }        return true;    }

Mmessages: the first message of the current sequence. The source code analysis of messagequeue does not maintain the relationship between many messages. This may save a lot of effort, but the relationship between messages.
All of them are thrown to the message itself for maintenance. This can be understood from the source code analysis of the message.
Midlehandler: stores a series of handler sets.
Final Boolean enqueuemessage (Message MSG, long when ),
This method is the method used by handler to process messages. It is very important for her to understand it. It puts the short delay time in front according to the delay time, the function code is as follows:

msg.when = when;Message p = mMessages;if (p == null || when == 0 || when < p.when) {       msg.next = p;       mMessages = msg;       this.notify();} else {      Message prev = null;      while (p != null && p.when <= when) {               prev = p;               p = p.next;           }       msg.next = prev.next;       prev.next = msg;       this.notify();}

When a message is added to messagequeue, check whether the current message (mmessage) is empty,
If it is null, when = 0, or when <p. when: the message (MSG) to be added is assigned to the current message (mmessage), and the MSG. the next attribute is set to null,

If it is not blank, the next message (next) of the current message (mmessage) is traversed cyclically, and the current message is remembered using Prev, the loop is exited when the next message of Prev is found to be null, And the MSG is connected to the back end of Prev, that is, this code: Prev. next = MSG;

Of course, you must note that an idlehandler interface is defined in it, which has only one method declaration: Boolean queueidle (). The addidlehandler (idlehandler handler) function in the messagequeue class is used to add idlehandler when the queue is empty. When the queue is empty, the idlehandler needs to be notified one by one.

4. Logoff

-- This class is used to maintain a message queue for a thread.
-- The default thread is not associated with a logoff instance. You need to use loop. Prepare () in the thread; associate it with a thread, and use logoff. Loop () to process the message until the end.

Logoff acts as a bridge between handler and message queue. The program component first passes the message to logoff through handler, and logoff puts the message into the queue. Logoff also broadcasts messages in the message queue to all handler. handler receives the messages and calls handlemessage for processing.
1) You can use the logoff static method logoff. mylogoff to obtain the logoff instance of the current thread. If the current thread has not joined a logoff instance, this method will return null.
2) You can use the static Looper. getmainlo method to obtain the looper instance of the main thread.

/**  * Class used to run a message loop for a thread.  Threads by default do  * not have a message loop associated with them; to create one, call  * {@link #prepare} in the thread that is to run the loop, and then  * {@link #loop} to have it process messages until the loop is stopped.  *   * <p>Most interaction with a message loop is through the  * {@link Handler} class.  *   * <p>This is a typical example of the implementation of a Looper thread,  * using the separation of {@link #prepare} and {@link #loop} to create an  * initial Handler to communicate with the Looper.  *   * <pre>  *  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();  *      }  *  }</pre>  */

public class Looper {    private static final boolean DEBUG = false;    private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;    // sThreadLocal.get() will return null unless you've called prepare().    private static final ThreadLocal sThreadLocal = new ThreadLocal();    final MessageQueue mQueue;    volatile boolean mRun;    Thread mThread;    private Printer mLogging = null;    private static Looper mMainLooper = null;     /** Initialize the current thread as a looper.      * This gives you a chance to create handlers that then reference      * this looper, before actually starting the loop. Be sure to call      * {@link #loop()} after calling this method, and end it by calling      * {@link #quit()}.      */    public static final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());}/** Initialize the current thread as a looper, marking it as an application's main      *  looper. The main looper for your application is created by the Android environment,     *  so you should never need to call this function yourself.     * {@link #prepare()}     */         public static final void prepareMainLooper() {        prepare();        setMainLooper(myLooper());        if (Process.supportsProcesses()) {            myLooper().mQueue.mQuitAllowed = false;        }    }    private synchronized static void setMainLooper(Looper looper) {        mMainLooper = looper;    }        /** Returns the application's main looper, which lives in the main thread of the application.     */    public synchronized static final Looper getMainLooper() {        return mMainLooper;    }public static final void loop() {        Looper me = myLooper();        MessageQueue queue = me.mQueue;        while (true) {            Message msg = queue.next(); // might block            if (msg != null) {                if (msg.target == null) {                    return;                }                if (me.mLogging!= null) me.mLogging.println(                        ">>>>> Dispatching to " + msg.target + " "                        + msg.callback + ": " + msg.what                        );                msg.target.dispatchMessage(msg);                if (me.mLogging!= null) me.mLogging.println(                        "<<<<< Finished to    " + msg.target + " "                        + msg.callback);                msg.recycle();            }        }    }/**     * Return the Looper object associated with the current thread.  Returns     * null if the calling thread is not associated with a Looper.     */    public static final Looper myLooper() {        return (Looper)sThreadLocal.get();    }

From the source code, we can see the logoff encapsulation information:
Logoff is essentially an encapsulation of the current thread, threadlocal, and messagequeue, that is, a Cycler responsible for transmitting messages between multiple threads.

When you add a message to handler, the method: public static final void loop () will be called by the system. The function code is as follows:
Msg.tar get. dispatchmessage (MSG), a message (MSG) is obtained from messagequeue, and then the dispatchmessage (MSG) of handler is called. The actual internal call of this method is handler. handlemessage (MSG) method. This is the method we want to override in the activity, so we can get the message passed by other sub-threads.

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.