Android asynchronous Message processing mechanism allows you to deeply understand the relationships among logoff, Handler, and Message, and androidlogoff

Source: Internet
Author: User

Android asynchronous Message processing mechanism allows you to deeply understand the relationships among logoff, Handler, and Message, and androidlogoff

Reprinted please indicate the source: http://blog.csdn.net/lmj623565791/article/details/38377229, this article from [Zhang Hongyang's blog]

Many people have been asked about the interview. What is the relationship between logoff, Handler, and Message in Android? The purpose of this blog is to first introduce the three-person relationship from the source code perspective, and then give a conclusion that is easy to remember.

1. Overview Handler, logoff, and Message are all concepts related to Android asynchronous Message processing threads. So what is an asynchronous message processing thread?
After the asynchronous message processing thread starts, it enters an infinite loop body. Each cycle is performed, a message is taken from its internal message queue, and the corresponding message processing function is called back, after a message is executed, the loop continues. If the message queue is empty, the thread will block the wait.

What is the relationship between Handler, logoff, and Message? In fact, logoff is responsible for creating a MessageQueue, and then entering an infinite loop body constantly reading messages from the MessageQueue, and the message creator is one or more Handler.

2. source code parsing 1. logoff mainly uses the prepare () and loop () methods for Logoff.
First, check the prepare () method.
public static final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(true));}

SThreadLocal is a ThreadLocal object that can store variables in a thread. As you can see, in row 5th, a loose instance is put into ThreadLocal, and row 2-4 determines whether sThreadLocal is null, otherwise an exception is thrown. This means that the logoff. prepare () method cannot be called twice. It also ensures that there is only one logoff instance in a thread ~ I believe some of my friends must have encountered this error.
The following describes the logoff constructor:
private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mRun = true;        mThread = Thread.currentThread();}
A MessageQueue (Message Queue) is created in the constructor ).
Then let's look at the loop () method:
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) {                // 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);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // 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.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            msg.recycle();        }}

Row 3:
Public static logoff mylogoff (){
Return sThreadLocal. get ();
}
The method directly returns the logoff instance stored in sThreadLocal. If me is null, an exception is thrown, that is, the logoff method must be run after the prepare method.
Row 6th: Get the mQueue (Message Queue) in the logoff instance)
Lines 13 to 45: This is what we call an infinite loop.
Row 14: Retrieves a message. If there is no message, it is blocked.
Line 27: Use msg.tar get. dispatchMessage (msg) to send the message to the dispatchMessage method of the target of msg for processing. What is the target of Msg? It is actually a handler object, which will be analyzed below.
Row 44: releases resources occupied by messages.

Logoff:
1. bind with the current thread to ensure that only one logoff instance is available for one thread, and only one MessageQueue is available for one logoff instance.
2. The loop () method constantly retrieves messages from MessageQueue and submits them to the dispatchMessage of the target attribute of the message for processing.
Well, our asynchronous message processing thread already has MessageQueue and the buddy who retrieves messages in the infinite loop body. What is missing now is the message sending object, so: Handler is on stage.

2. Before Handler uses Handler, We Initialize an instance. For example, if it is used to update the UI thread, We Will initialize it directly at the time of declaration or initialize the Handler instance in onCreate. So let's first look at the Handler's construction method and how it relates to MessageQueue, and how the messages it sends in the Child thread (generally, messages are sent in non-UI threads) are sent to MessageQueue.
public Handler() {        this(null, false);}public Handler(Callback callback, boolean async) {        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 = callback;        mAsynchronous = async;    }

Row 14: Use logoff. mylogoff () obtains the logoff instance saved by the current thread, and then obtains the MessageQueue (Message Queue) saved in the logoff instance in row 19 ), this ensures that the handler instance is associated with MessageQueue in our loose instance.

Then let's look at our most common sendMessage method.

   public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }

   public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageDelayed(msg, delayMillis);    }

 public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

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

Finally, sendMessageAtTime is called. In this method, MessageQueue is directly obtained and the enqueueMessage method is called. Let's take a look at this method:

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

In enqueuemessage, the value of meg.tar get is set to this first. [if you still remember that The logoff loop method will retrieve each msg and hand it to msg, target. dispatchMessage (msg) to process messages], that is, the current handler is used as the target attribute of msg. In the end, the queue's enqueueMessage method will be called, that is, the messages sent by handler will be saved to the message queue.


Now it is clear that logoff will call the prepare () and loop () methods and save a logoff instance in the current thread. This instance will save a MessageQueue object, then, the current thread enters an infinite loop and constantly reads the messages sent by Handler from MessageQueue. Then call back the dispathMessage method in the handler that creates the message. Let's take a look at this method as soon as possible:

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

As you can see, Row 3 calls the handleMessage method. Let's look at this method:

  /**     * Subclasses must implement this to receive messages.     */    public void handleMessage(Message msg) {    }    
We can see that this is an empty method. Why? Because the final callback of a message is controlled by us, we rewrite the handleMessage method when creating a handler, and then follow msg. what is used for message processing.

For example:

private Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg){switch (msg.what){case value:break;default:break;}};};

At this point, this process has been explained. Let's summarize it first.

1. First, logoff. prepare () saves a logoff instance in this thread, and then saves a MessageQueue object in this instance because of logoff. prepare () can be called only once in a thread, so MessageQueue only has one.

Initialize get. dispatchMessage (msg) method.

3. the Handler constructor will first obtain the logoff instance saved in the current thread and associate it with MessageQueue In The logoff instance.

4. The sendMessage method of Handler will assign the target value of msg to handler itself and add it to MessageQueue.

When constructing a handlerance construct in step 5, we will re-write handlemessagemessages, and parse the method finally called by msg.tar get. dispatchMessage (msg.

Well, the summary is complete. You may also ask, so in the Activity, we did not display the call logoff. prepare () and logoff. loop () method. Why can Handler be successfully created? This is because logoff has been called in the current UI thread in the startup code of the Activity. prepare () and logoff. loop () method.

3. Handler post

Someone asked me today, what is the relationship between the thread created by the post method of Handler and the UI thread?

In fact, this problem is also one of the reasons for this blog; here we need to explain, sometimes for convenience, we will directly write the following code:

mHandler.post(new Runnable(){@Overridepublic void run(){Log.e("TAG", Thread.currentThread().getName());mTxt.setText("yoxi");}});

Then, you can write the code for updating the UI in the run method. In fact, this Runnable does not create any threads, but sends a message. See the source code below:

 public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }
  private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

We can see that a Message object is obtained in getPostMessage, And the created Runable object is assigned to this message as the callback attribute.

Note: a Message object can be generated, either new or Message. the obtain () method; both of them are acceptable, but we recommend that you use the obtain method because the Message maintains a Message pool for Message reuse, avoiding the use of new to re-allocate memory.

 public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

 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);    }
The final mode is similar to handler.sendmessage, and sendmessageattimeis used. Then enqueuemessagehandler is used, msg.tar get is assigned as handler, and MessagQueue is added.

We can see that both the callback and target values of msg have values, so which one will be executed?

In fact, the above Code has been pasted, that is, the dispatchMessage method:

 public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }
Row 3: if it is not null, the callback is executed, which is our Runnable object.

Well, the relationship between logoff, Handler, and Message is clearly described above.

Finally, let's look at the illustration:


I hope images can help you better remember them ~~

4. Post

In fact, Handler can not only update the UI, but you can create a Handler in a sub-thread and then use this handler instance to send messages in any other thread, the final message processing code will be run in the thread where you create the Handler instance.

new Thread(){private Handler handler;public void run(){Looper.prepare();Looper.loop();handler = new Handler(){public void handleMessage(android.os.Message msg){Log.e("TAG",Thread.currentThread().getName());};};};}.start();

Android not only provides an asynchronous message processing mechanism for us to better update the UI, but also provides a reference for the code of the asynchronous message processing mechanism ~~ Not only can you know the principle, but you can also use this design in other non-Android projects ~~





Principle of android handler Mechanism

Andriod provides Handler and logoff to satisfy inter-thread communication.
Handler first-in-first-out principle.
The logoff class is used to manage Message Exchange between objects in a specific thread ).

1) loue: A thread can generate a loue object to manage the Message Queue (Message Queue) in this thread ).

2) Handler: You can construct a Handler object to communicate with logoff so as to push new messages to the Message Queue or receive messages sent by logoff from the Message Queue.

3) Message Queue: used to store messages placed by threads.

4) thread: the UI thread is usually the main thread, and Android will create a Message Queue for it when starting the program.
 
For details about the android message processing mechanism

Next, I would like to share with you the very important Message processing mechanism in android. We must understand the functions of Message processing, Message, MessageQueue, logoff, and Handler.

Next we will talk about their respective functions:

MessageQueue

MessageQueue indicates the message queue where messages are stored and executed according to the "first-in-first-out" rule. Each thread can have only one MessageQueue. A MessageQueue object is created when a logoff object is created.

Message

Message indicates the Message object, which is stored in MessageQueue. Multiple Message objects can be stored in one MessageQueue. You can call the obtain () method of the Message class or call the obtainMessage () method of the Handler class to obtain the Message object. However, this does not necessarily create a new Message object, if there is an available Message object in the Message pool, this object is directly taken out and returned. Otherwise, if there is no available Message object in the Message pool, a new Message object is created. After the Message object in the Message queue is processed by the system, the Message object will be deleted from MessageQueue and placed in the Message pool.

Logoff

Logoff is used to operate MessageQueue. Each logoff corresponds to a MessageQueue. You can call logoff. the mylomessage () method gets the Looper object of the current thread. lomessage fetches the Message object from MessageQueue cyclically and submits it to Handler to call the handleMessage method for processing. After processing, the Message object is placed in the Message pool.

Handler

Handler is the Message processor. Handler encapsulates the information to be passed into a Message object and calls the sendMessage method to put the Message into MessageQueue. When MessageQueue loops to the Message, call the handleMessage method of the corresponding Handler object to process it. D-android.com/
Note that by default, The logoff object and MessageQueue object will be created in the UI thread, that is, the main thread. To process messages in our new thread, we must create the logoff object and MessageQueue object, by calling logoff. the prepare () method can be used to create a logoff object and a MessageQueue object, and call logoff. the loop () method can start the message loop queue.
Here, d-android.com/#/thread-25343-1-1.htmlis also an example. You can refer to the example and ask for distribution !!!

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.