Android asynchronous message processing mechanism (2) Source Code Analysis

Source: Internet
Author: User

Android asynchronous message processing mechanism (2) Source Code Analysis

 

Sorting by pinyin in sequence, irrelevant to the technology itself.

First, let's briefly summarize the relationships among logoff, MessageQueue, Message, and Handler:

Logoff and MessageQueue
The logoff object is the message loop processor of a thread. Each thread can have only one logoff. Logoff has a MessageQueue object in the message queue. All messages of this thread are stored in this queue (sorted by time. Android automatically creates a logoff object for the main thread (UI thread) at startup, and we must create a logoff object (call Looper.prepare()). Handler (non-abstract class)
The Handler object is the receiver and Handler of the Message object. The user adds a message to the Message Queue through Handler and calls back the message through Handler. hanldeMessage()Process messages. Hanlder is associated with a logoff object during construction. Handler and Looper are multi-to-one relationships. Multiple Handler objects can establish relationships with the same Looper object, but not vice versa. Message
Message is the Message carrier and a derived class of Parcelable. The main member variables and methods involved in The logoff class are as follows:
Public final class logoff {// member variable static final ThreadLocal
  
   
SThreadLocal = new ThreadLocal
   
    
(); Private static Looper smainloue; final MessageQueue mQueue; final Thread mThread; // The public static void prepare (){...} private static void prepare (boolean quitAllowed ){...} public static void preparemainlogoff (){...} public static Looper getMainLooper (){...} public static void loop (){...} public static logoff mylogoff (){...} public static MessageQueue myQueue (){...}}
   
  
Main member methods in the MessageQueue class:
public final class MessageQueue {    Message next() {...}    boolean enqueueMessage(Message msg, long when){...}}
Main member variables and methods in the Handler class:
Public class Handler {// Internal interface public interface Callback {public boolean handleMessage (Message msg);} // Method for Message Processing public void handleMessage (Message msg) {} public void dispatchMessage (Message msg ){...} private static void handleCallback (Message message ){...} // constructor related method public Handler (){...} public Handler (Callback callback ){...} public Handler (low.logoff ){...} public Handler (lookloback, Callback callback ){...} public Handler (boolean async ){...} public Handler (Callback callback, boolean async ){...} public Handler (lookloback, Callback callback, boolean async ){...} // obtain the Message-related method public final Message obtainMessage (){...} public final Message obtainMessage (int what ){...} public final Message obtainMessage (int what, Object obj ){...} public final Message obtainMessage (int what, int arg1, int arg2 ){...} public final Message obtainMessage (int what, int arg1, int arg2, Object obj ){...} // post-related method public final boolean post (Runnable r ){...} public final boolean postAtTime (Runnable r, long uptimeMillis ){...} public final boolean postAtTime (Runnable r, Object token, long uptimeMillis ){...} public final boolean postDelayed (Runnable r, long delayMillis ){...} public final boolean postAtFrontOfQueue (Runnable r ){...} // send related method public final boolean sendMessage (Message msg ){...} public final boolean sendEmptyMessage (int what ){...} public final boolean sendEmptyMessageDelayed (int what, long delayMillis ){...} public final boolean sendEmptyMessageAtTime (int what, long uptimeMillis ){...} public final boolean sendMessageDelayed (Message msg, long delayMillis ){...} public boolean sendMessageAtTime (Message msg, long uptimeMillis ){...} public final boolean sendMessageAtFrontOfQueue (Message msg ){...} // private boolean enqueueMessage (MessageQueue queue, Message msg, long uptimeMillis ){...}}
Main member variables and methods in the Message class:
Public final class Message implements Parcelable {// main member variable public int what; public int arg1; public int arg2; public Object obj; public Messenger replyTo; private static final Object sPoolSync = new Object (); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; // public Message () {} // It is not recommended to use // obtain the Message-related method public static Message obtain (){...} public static Message obtain (Message orig ){...} public static Message obtain (Handler h ){...} public static Message obtain (Handler h, Runnable callback ){...} public static Message obtain (Handler h, int what ){...} public static Message obtain (Handler h, int what, Object obj ){...} public static Message obtain (Handler h, int what, int arg1, int arg2 ){...} public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj ){...} public void setTarget (Handler target ){...} public void sendToTarget (){...} public Handler getTarget (){...}}

The following describes the asynchronous message processing mechanism in question and answer mode.

Q & A Analysis 1. What is the role of ThreadLocal?

Resolution: ThreadLocal is the meaning of Thread Local Variable, that is, the Local Variable of the Thread. by placing the data in ThreadLocal, each Thread can create a copy of the Variable, this avoids concurrent access thread security issues. Here, the Instance Object of the logoff class is saved.

2. Why doesn't Handler be instantiated in the UI thread? Looper.prepare()In the sub-thread Looper.prepare()?

Resolution: this is because in the ActivityThread classmain()Method calledLooper.prepareMainLooper()The simple code is as follows:

Public static void main (String [] args ){... // logoff is omitted. prepareMainLooper (); ActivityThread thread = new ActivityThread (); thread. attach (false); if (sMainThreadHandler = null) {sMainThreadHandler = thread. getHandler () ;}if (false) {loler. mylogoff (). setMessageLogging (new LogPrinter (Log. DEBUG, ActivityThread);} logoff. loop (); throw new RuntimeException (Main thread loop unexpectedly exited );}

We can see thatmain()Method calledLooper.prepareMainLooper(), AndLooper.prepareMainLooper()Source code:

Public static void prepareMainLooper () {prepare (false); synchronized (Looper. class) {if (smainloed! = Null) {throw new IllegalStateException (The main Looper has already been prepared .);} // If a logoff object already exists, directly obtain smainlogoff = mylogoff () ;}} from the ThreadLocal object sThreadLocal ();}}

Whileprepare(boolean)The Code is as follows:

 private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException(Only one Looper may be created per thread);        }        sThreadLocal.set(new Looper(quitAllowed));    }

Here we can seemain()Method calledLooper.prepareMainLooper(), AndLooper.prepareMainLooper()Called againprepare(boolean).prepare(boolean)Method will create a loose object and save it in the static variable sThreadLocal (ThreadLocal class instance ). Let's take a look at the logoff constructor:

private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

The constructor is private and creates a Message Queue instance and saves the current thread.

At the same timeprepare(boolean)It can be seen that if sThreadLocal already has a logoff object and is created again, an exception will occur. This shows two points: 1. One thread can only have one loose object; 2. One thread can only be called once at most.prepare()Prepare-related methods (prepare(),prepare(boolean),prepareMainLooper()(Called in the UI thread )).

What we call in the Child thread isLooper.prepare(), Let's take a look at the source code of this method:

public static void prepare() {        prepare(true);    }

We can see that it is also called.prepare(boolean).

3. What does the Handler system do for us?

Resolution: The problem mainly involves the following methods in Handler:

public Handler() {this(null, false);}public Handler(Callback callback) {this(callback, false);}public Handler(Looper looper) {this(looper, null, false);}public Handler(Looper looper, Callback callback) {this(looper, callback, false);}public Handler(boolean async) {this(null, async);}public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class
   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;    }public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

The following parameters may be passed in the constructor:

Logoff object, used to specify the logoff object that Handler sends messages. Therefore, you must call the Handler object before instantiating it. Looper.prepare()Method. Callback interface object, used to process messages sent by Handler. Boolean value, whether it is an asynchronous operation. For Handler, the value here is false.

FirstHandler(Callback callback, boolean async)We can see that the calledLooper.myLooper()Returns the logoff object. If not, an exception is thrown.

Based on the above constructor, We Can instantiate a handler object in multiple ways. The first five constructor will eventually call the following two constructor. ForHandler(Callback callback, boolean async)If the constructor does not specify the Looper object of Handler, the Looper object of the current thread is used. At the same time, we can see that the Callback interface object is passed and the value is assigned to mCallback. Let's take a look at the relevant source code:

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

The code above shows that if the Callback interface object is passeddispatchMessage(Message msg)Will not be executedhandleMessage(msg)(Before that, determine whether msg. callback is empty, and then talk about it), but callmCallback.handleMessage(msg)This method is the implementation method of the Callback interface object passed. Therefore, we can also process messages in this way. If the Callback interface object is not passed,handleMessage(msg)You can use Handler to process messages.

4. Why do I call the Handler object after it is instantiated? Looper.loop()?

To solve this problem, let's take a look.loop()Source code:

Public static void loop () {final Looper me = myLooper (); if (me = null) {throw new RuntimeException (No lotime; loal. prepare () wasn' t called on this thread .);} final MessageQueue queue = me. mQueue ;..... for (;) {// get a Message. If it is not found, the Message msg = queue will be blocked. next (); // might block if (msg = null) {// msg is equal to null, return will be exited ;}..... msg.tar get. dispatchMessage (msg );..... msg. recycleUnchecked (); // ensure elimination Information Cycle. }}

Inloop()First obtain the logoff object me, and then obtain the corresponding message queue Based on this object. Next, enter a(;;)Messages are continuously retrieved from the queue in an endless loop.next()(This method is complicated, so I didn't understand it very well.) The method will be blocked.
If the message is empty, it exits.loop(); Below is a sentence:msg.target.dispatchMessage(msg);The target attribute of the message msg is the Handler object, and the message is processed by the Handler. We can use the Message overload methodobtainXXX()AndsetTarget()Set the Handler object.

Passloop()To continuously process the sent messages.

Let's take a look at the MessagesendToTarget():

 public void sendToTarget() {        target.sendMessage(this);    }

You can see that the same method is used to send messages as to send messages through Handler.

5. In Handler sendMessage()How does one send messages to a message queue and manage messages?

First, let's take a look,sendRelated methods:

// Send related method public final boolean sendMessage (Message msg ){...} // The message to be sent needs to be processed in a timely manner, but the public final boolean sendEmptyMessage (int what ){...} // only send messages with the "what" attribute. If you want to process the message in a timely manner, public final boolean sendEmptyMessageDelayed (int what, long delayMillis ){...} // you want to delay public final boolean sendEmptyMessageAtTime (int what, long uptimeMillis ){...} // you want the Message to process public final boolean sendMessageDelayed (Message msg, long delayMillis) {...} at the specified time ){...} // you want to delay public boolean sendMessageAtTime (Message msg, long uptimeMillis ){...} // you want the Message to process public final boolean sendMessageAtFrontOfQueue (Message msg) {...} at the specified time ){...} // The message is queued and can be processed immediately

WithEmptyMethod or even send messages,what!! Note.

The abovesendMethod,sendMessageAtFrontOfQueue(Message), Will callsendMessageAtTime(Message,long). The two methods return the message queue'senqueueMessage(Message,long)Is to insert messages to the message queue in chronological order. In fact, the message queue is implemented by a linked list, and the sorting method is arranged by time.enqueueMessage(Message,long)The source code is as follows:

boolean enqueueMessage(Message msg, long when) {       ....        synchronized (this) {           ....            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                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;            }            ....        }        return true;    }

You can see thatifDetermine whether the time of the inserted message is the minimum. If so, insert the message directly to the head of the linked list; otherwise, enterfor (;;)Traverse the table in sequence until the time is less than the linked list value, and then insert it again. Message Queues are saved as mMessages headers.

The above method uses Handler'ssendMethod To insert a message into a message queue. In this case, it is still in the same thread.

6. How is the conversion between threads? After the Handler object is instantiated in thread A, call loop()Method cyclically checks whether a message enters the queue. If yes, It is retrieved and processed. In thread B sendMessage()Method to send A message. The sent message enters the Message Queue through A series of processing, so that the message enters the thread.

The communication between thread A and thread B is realized through the above two steps.
In which thread, how does one retrieve messages and how does one block messages?

7. Message Creation Method

There are many ways to create messages. Let's compare them:

Handler's obtainMessage()Series; static Message Methods obtain()Series; create with constructor.

The methods in Handler are actually static methods that call Message.obtain()Series, andobtain()The series will eventually callobtain()Method. Let's take a look at its source code:

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

When we can see that there are redundant messages, we can use them directly, instead of creating a new message, and use messages cyclically to reduce resource waste. The reason why messages can be recycled is that messages are called every time a message is processedrecycleUnchecked()This method saves the used messages. The source code is as follows:

Void recycleUnchecked () {... // clear the message content synchronized (sPoolSync) {if (sPoolSize <MAX_POOL_SIZE) {next = sPool; sPool = this; sPoolSize ++ ;}}}

SPool is a static member variable that stores the head of the linked list. When a message is processed (when the size of the message pool is smaller than the given value 50), the message is inserted into the linked list using the header insertion method.

8. Handler's postWhat is the method used?

Let's take a look at it.dispatchMessage()Method, which has a judgment:

if (msg.callback != null) {    handleCallback(msg);}

Only whenmsg.callbackIf it is null, the following statement is executed. The callback attribute of a message is a Runnable variable. Where is the value assigned for this attribute? We found thatpostA method will be called in the SeriesgetPostMessage():

private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

You can see that this method can assign values to the callback attribute, that is, throughpostMethod to send messages andsendMessages can be processed in the same way (in factpostIs also calledsendMethod), but nothandleMessage()Process the message, but inhandleCallback()Process messages. The source code of this method will surprise us:

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

Directly calling the Runnable objectrun()In this way, instead of creating a new thread, it is called directly.

Of course, you can alsoobtain()Set the callback attribute.

postMethod and sendDifferences Between Methods

postThe method is called internally.sendMethod, andpostMethods send messages with processing methods, whilesendA Method sends messages that do not contain a processing method. The former processes messages in the method carried by itself, and the latter can only process messages throughhandleMessage()Processing

 

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.