This paper first analyzes the whole Android messaging mechanism from the whole architecture, and then introduces the functions and tasks of each component from the source point of view. The basic concepts are not introduced, and the mechanism of threadlacal and garbage collection should be researched by ourselves.
Infrastructure Architecture
First, we need to look at the overall architecture to see what the Android thread communication does. As we all know, a process is the smallest unit of resources allocated by the operating system, a process in which multiple threads can be started to perform tasks that share the resources of a process without allocating resources, and the resources described here are primarily memory-only resources. The inter-threading messaging mechanism of Android is very similar to that of our real-life people, and we can compare the communication process of two people: suppose a wants to write to B, first write the letter in the envelope (message), to B, the postman (handler) in B's mailbox (MessageQueue), B's housekeeper (looper) found that a letter needs to be checked, to B to deal with. Is the rest of the threads sending messages to the main thread:
The whole process is as follows:
- A child thread sends a message to the main thread through the handler of the main thread;
- This thread is placed in the message queue of the main thread;
- The entire message queue is created and managed by Looper, and is removed to the main thread processing once a new message is found to be present.
Component Source Analysis
Having known the entire architecture, we've learned a little bit about the subtle design of the communication mechanism between Android threads from the source point of view.
Envelope Message
The message class is sent to handler, which encapsulates a description of the message and the data object that needs to be passed. About the message recycling mechanism we put it in the following article, which is used only as the message class that encapsulates the data to be passed. First, two int member variables and one obj object are used to store the data being passed:
- What: User-defined message code for the message recipient to identify the type of the message.
- Arg1, arg2: If you only pass int values, you can store them with these two parameters.
- OBJ: You can store data objects that need to be passed.
There are also some member variables that need to be understood briefly:
- INT flags: Whether the message is being taken
- Long When: timestamp
- Bundle Data: The information to be passed
- Handler Target: Specifies the destination Handler that handles the message.
- Runnable callback: You can also specify a callback function that handles the message
- Message Next: Point to the next message, which is described in more detail later in this MessageQueue.
You can obtain a message object using the Obtain method (Factory mode):
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 ();
There are also some access methods for member variables, not all of them, we use only need to get to the message object, the data will be passed into the object is OK. Then we need to call the Sendtotarget method to send this message out:
/** * Sends this Message to the Handler specified by {@link #getTarget}. * Throws a NULL pointer exception if this field have not been set. * /public void Sendtotarget () { target.sendmessage (this); }
Of course, you have to indicate to which handler to handle the message, otherwise you will be given a null pointer. Method calls the SendMessage method of handler, let's look at handler.
Postman Handler
Handler is not just like the traditional postman, because handler is responsible for sending messages to the main thread in the MessageQueue, and is responsible for the main thread from the Looper to take out the message to handle, have you ever seen such a thoughtful postman? A handler instance is a single thread and its Message Queuing service, and when you create an handler instance object in a thread, the handler object is bound to the thread and its message queue and begins servicing them. Another thing to mention is that the message here is not just about data, it can be a runnable object that can be executed by the main thread.
When the application's process is created, its main thread maintains a message queue for managing those top-level application components (Activity,broadcast receiver, etc.) and the created window. The threads you open can communicate with the main thread through handler, perform tasks at the right time, or process messages, and so on.
First, let's look at how to create a handler, constructed as follows:
Public Handler (Callback Callback, Boolean async) { if (find_potential_leaks) { final class<? extends handler& Gt 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 ();//Gets the Looper if (mlooper = = null) of the current thread { throw new RuntimeException ( "Can ' t Create handler inside thread that have not called looper.prepare () "); } Mqueue = mlooper.mqueue;//Get to looper managed message Queue Mcallback = callback;//Custom callback function masynchronous = async; }
We'll talk about the memory leaks later. Let's focus on what our handler did when it was created. When we talk about the whole communication mechanism, we say that a thread can have only one looper to poll for a single message queue, that is, a thread, a looper, and a message queue that has a relationship of one by one. Between handler and Looper is a one-to-many relationship, that is, a looper can be served by multiple handler. In Handler's constructor, the main task is to associate handler with the Looper that it serves, the message queue.
Handler can send messages to the queue or add an executable runnable object, and Message Queuing schedules message processing at some point or executes the Runnable object Run method:
- Post (Runnable R): Joins the Runnable object to the message queue, and the Runnable object will be executed by the thread of the message queue.
- Postattime (Runnable, long): Joins the Runnable object to the message queue and executes the Runnable object at the specified time.
- Postdelayed (Runnable R, Long Delaymillis): Joins the Runnable object to the message queue and executes after the specified delay time.
- Sendemptymessage (int): Sends a message that contains only what value to the message queue
- SendMessage (Message msg): Adding a message to the message queue
- Sendmessageattime (Message msg, long Uptimemillis): joins a message to the tail of the queue at a specified time
- sendmessagedelayed (Message msg, long Delaymillis): Adds a message to the tail of the queue after a specified time delay
With these methods, handler the message object to MessageQueue. Looper the message is removed from the messages queue, it calls the Handlerdispatch method that the message holds and distributes it to handler to process the message:
public void DispatchMessage (Message msg) { if (msg.callback! = null) {///If the Message object holds a callback object, execute it handlecallback ( msg); } else { if (mcallback! = null) {//If the current handler holds a callback function for message handling, it executes if (Mcallback.handlemessage (msg)) { return;< c7/>} } handlemessage (msg);//If none, execute user-defined message handling method } }
Butler Looper and mailbox MessageQueue
From the above analysis, we can understand that the message object is joined to the message queue indirectly through handler, and then messages queue is organized into a queue for looper distribution. If we just look at the name, we'll guess that this class uses the data structure of the queue to organize the message, but that's not the case. The MessageQueue organizes the message objects in chronological order into a list of forms to manage.
When creating the handler object, we get the Looper object corresponding to the current thread one by one through the Looper.mylooper method from the threadlocal. So when did the front-thread create the entire Looper object and put it into threadlocal? In the Android UI thread, we have already created the Looper for us, and if we create a thread ourselves, we need to call the Prepare method to create the Looper object:
public static void Prepare () { prepare (true); } 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)); }
When the Looper object is created, it also creates a message queue to poll, and obtains a reference to the current thread:
Private Looper (Boolean quitallowed) { mqueue = new MessageQueue (quitallowed); Mthread = Thread.CurrentThread (); }
Since the thread and the Looper object are one by one corresponding relationships, we sometimes judge whether the current thread is a UI thread or not, and call the Getmainlooper method to determine:
public static Looper Getmainlooper () { synchronized (looper.class) { return smainlooper; }}
The Looper object holds the object that it polls for the message queue and polls through 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;//gets to the current message queue//Make sure the identity of this thread is the the local proce SS,//And keep track of the What's identity token actually is . Binder.clearcallingidentity (); Final Long ident = Binder.clearcallingidentity (); Start polling Message Queuing for (;;) {
Gets the next message from the message queue, which may block
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, 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); }//Send the message from the queue to handler to handle 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.tohexstring (Ident) + "to 0x" + Lon G.tohexstring (newident) + "while dispatching to" + Msg.target.getClass (). GetName () + "" + Msg.callback + "what=" + msg.what); }//Flag the message has been processed and can be recycled msg.recycleunchecked (); } }
So far, the message object created in the child thread has been processed. Now we need to talk about handler's leaks and how to fix them.
handler leak problem
Before we talk about the whole problem, we need to understand the GC mechanism of Java, which is not described in detail here. When we create a handler object in activity, we tend to inherit an anonymous inner class that handler the Handlemessage method. At this point, the anonymous inner class holds the object reference for the current activity. At the same time, the child thread object holding the handler tends to take some time-consuming actions, creating a Message object and assigning a reference to the handler object to it. If the user closes the activity, the activity object should be reclaimed by the system. However, due to the time-consuming operation of the child thread, and the fact that both it and the unhandled message object hold the handler reference, and handler holds the activity reference, this causes the activity to fail to be reclaimed and a memory leak occurs.
There are currently two main solutions:
- First, when the activity shuts down, the child thread is stopped, and then the Removecallbacks method of the handler is called to remove the message from the messages queue.
- Second, the anonymous inner class appears as a static inner class so that it does not hold the object reference of the activity and the activity can be recycled. However, how do you manipulate objects without a reference to activity? I have to declare a weak reference:
Static class MyHandler extends Handler { weakreference<activity > mactivityreference; MyHandler (activity activity) { mactivityreference= new weakreference<activity> (activity); } @Override public void handlemessage (Message msg) { //messages processed ... }}
Weak references are ignored during garbage collection, so they can be safely recycled. Individuals tend to prefer the second way of writing, relatively simple.
Summary
This paper briefly introduces the mechanism of asynchronous communication between the threads of Android system, and briefly discusses the basic work of thread communication from the point of source. It is not detailed into the specific management of the MessageQueue, but simply refers to the collection of the Message object, the details are free to fill up.
Category: Android application Framewo
"Go" Android message mechanism