Android thread asynchronous communication mechanism source code analysis, android source code

Source: Internet
Author: User

Android thread asynchronous communication mechanism source code analysis, android source code

This article first analyzes the message transmission mechanism between threads in Android from the overall architecture, and then introduces the functions and tasks of each component from the source code perspective. The basic concepts are not introduced in this article. Please study the threadLacal and garbage collection mechanisms on your own.

Infrastructure

First, we need to understand what the Android thread communication has done from the overall architecture. We all know that a process is the minimum unit for the operating system to allocate resources. A process can start multiple threads to execute tasks. These threads can share the resources of the process but do not allocate resources, the resources mentioned here are mainly memory resources. The message transmission mechanism between Android threads is very similar to that between people in our real life. We can compare the communication process between two people: assume A wants to write A letter to B, first, write the mail into the envelope and hand it to the handler of B. In the messageQueue of B, B's manager finds that there are emails to be checked, it will be handed over to B for processing. Messages are sent by other threads to the main thread:

The process is as follows:

Component source code analysis

After learning about the entire architecture, let's take a look at the subtle design of the communication mechanism between Android threads from the source code perspective.

Envelope message

The Message class is the Message sent to handler. It encapsulates a description of the Message and the data objects to be transmitted. We will introduce the message collection mechanism in a later article. Here we will only use it as a message class that encapsulates the data to be transmitted. First, two int member variables and an obj object are used to store transmitted data:

    • What: a custom message code used by the message receiver to identify the type of the message.
    • Arg1 and arg2: If only int values are passed, these two parameters can be used for storage.
    • Obj: stores data objects to be passed.

Some member variables also need a brief understanding:

    • Int flags: whether the message is being used
    • Long when: Timestamp
    • Bundle data: data to be transmitted
    • Handler target: Specifies the target handler for processing the message.
    • Runnable callback: You can also specify the callback function for processing the message.
    • Message next: point to the next message, which will be detailed later when messageQueue is introduced.

 

The obtain method can be used to obtain a message object (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, so we will not introduce them one by one. We only need to get the message object when using it, and it will be OK to put the data to be passed into this object. Then we need to call the sendToTarget method to send the message:

/**     * Sends this Message to the Handler specified by {@link #getTarget}.     * Throws a null pointer exception if this field has not been set.     */    public void sendToTarget() {        target.sendMessage(this);    }

Of course, you must specify the handler to which the message will be sent for processing. Otherwise, a null pointer will be reported. The sendMessage method of handler is called in the method. Next we will study handler.

Postman handler

Handler is not just a traditional postman, because handler is responsible for sending messages to the messageQueue of the main thread in the Child thread, he is also responsible for extracting messages from logoff in the main thread for processing. Have you ever seen such a thoughtful postman? A handler instance is only a thread and Its Message Queue Service. When you create a handler instance object in a thread, then the handler object is bound with the thread and its message queue and starts to serve them. In addition, the message here is not only an exponential data, but also a Runnable object that can be executed by the main thread.

After an application's process is created, its main thread maintains a message queue for managing the top-layer application components (such as activity and broadcast receiver ER) and created windows. The thread you open can communicate with the main thread through handler, execute the task at the right time, or process messages.

First, let's take a look at how to create a handler. The constructor is as follows:

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 shocould be static or leaks might occur:" + klass. getCanonicalName () ;}} mLooper = Looper. myloled (); // obtain the logoff if (mloif = null) of the current thread {throw new RuntimeException ("Can't create handler inside thread that has not called loled. prepare () ");} mQueue = mloue. mQueue; // get the message queue mCallback = callback managed by logoff; // custom callback function mAsynchronous = async ;}

We will discuss about memory leakage later. First, let's take a look at what handler did during creation. When talking about the entire communication mechanism, we have said that a thread can only have one logoff to round the only message queue, that is, the relationship among threads, logoff, and message queues is one-to-one. Handler and looper are one-to-multiple relationships, that is, a looper can be served by multiple handler. In the handler constructor, the main task is to associate handler with The logoff and Message Queue it wants to serve.

Handler can send messages to the queue or add an executable runnable object. The message queue schedules message processing at a certain time point or executes the runnable object run method:

    • Post (Runnable r): adds a runnable object to the Message Queue. This runnable object will be executed by the thread where the message queue is located.
    • PostAtTime (Runnable, long): adds the runnable object to the message queue and executes the runnable object at the specified time.
    • PostDelayed (Runnable r, long delayMillis): adds a runnable object to the message queue and runs after a specified delay.
    • SendEmptyMessage (int what): sends a message containing only what value to the message queue.
    • SendMessage (Message msg): adds a Message to the Message queue.
    • SendMessageAtTime (Message msg, long uptimeMillis): adds a Message to the end of the queue at a specified time.
    • SendMessageDelayed (Message msg, long delayMillis): adds a Message to the end of the queue after a specified delay.

Through these methods, handler handed over the message object to the messageQueue of the message queue. After logoff retrieves the message from the message queue, it will call the handlerdispatch method held by the message and distribute 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 handleCallback (msg);} else {if (mCallback! = Null) {// if the current handler holds the callback function for message processing, execute it if (mCallback. handleMessage (msg) {return ;}} handleMessage (msg); // If none exist, execute the custom message processing method }}

 

Manage logoff and MessageQueue

From the above analysis, we can understand that the message object is indirectly added to the message Queue through handler, and then the message object is organized into a queue and provided to logoff for distribution. If we only look at the name, we will assume that this class uses the queue data structure to organize messages, but this is not the case. MessageQueue organizes message objects into a linked list in chronological order for management.

When creating a handler object, we use the loler. myLooper method to obtain the looper object that corresponds to the current thread one by one from threadLocal. So when is the current thread creating the entire logoff object and putting it into threadLocal? In the Android UI thread, we have already automatically created a logoff for us. If it is a self-created thread, we need to call the prepare method to create a logoff object:

public static void prepare() {        prepare(true);    }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));  }

When creating a logoff object, it also creates a message queue for polling and obtains the reference of the current thread:

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

Because the thread and logoff object are in a one-to-one relationship, sometimes we call the getmainlogoff method to determine whether the current thread is a UI thread:

public static Looper getMainLooper() {        synchronized (Looper.class) {            return sMainLooper;        }}

The logoff object holds the object of the Message Queue it polls and uses the loop method for polling:

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; // get the current Message Queue // 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 (); // The (;;){
// Obtain the next message from the message queue, which may be blocked
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.tar get + "" + msg. callback + ":" + msg. what);} // send the message obtained from the queue to handler to process msg.tar get. dispatchMessage (msg); if (logging! = Null) {logging. println ("<Finished to" + msg.tar get + "" + msg. callback);} // Make sure that during the course of dispatching the // identity of the thread wasn't upted. 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.tar get. getClass (). getName () + "" + msg. callback + "what =" + msg. what);} // indicates that the message has been processed and can be recycled msg. recycleUnchecked ();}}

So far, the message object created in the Child thread has been processed. Next we need to talk about handler leakage and solutions.

Handler Leakage

Before talking about the entire problem, we need to understand the GC mechanism of JAVA. We will not detail it here. When we create a handler object in the activity, we often inherit an anonymous internal class, which rewrites the handleMessage method of handler. In this case, the anonymous internal class will hold the object reference of the current activity. At the same time, the sub-thread object holding the handler often performs some time-consuming operations, creates a message object, and assigns the reference of the handler object to it. If the user closes the activity, the activity object should be recycled by the system. However, because the time-consuming operation of the subthread and the unprocessed message object both hold the reference of handler and the handler holds the reference of activity, this will make the activity unable to be recycled, memory leakage occurs.

Currently, there are two main solutions:

  • First, when the activity is closed, stop the sub-thread and call the removeCallbacks method of handler to delete the message in the message queue.
  • Second, let the anonymous internal class appear as a static internal class, so that the activity can be recycled without holding the reference of the activity object. However, how do I operate the objects without referencing the activity? I had to declare a weak reference myself:
Static class myHandler extends Handler {WeakReference <Activity> mActivityReference; myHandler (Activity activity) {mActivityReference = new WeakReference <Activity> (activity) ;}@ Override public void handleMessage (Message msg) {// message processing ......}}

Weak references are ignored during garbage collection, so they can be safely recycled. I personally prefer the second method, which is relatively simple.

Summary

This article briefly introduces the asynchronous communication mechanism between threads in the Android system, and briefly talks about the basic work of thread communication from the source code perspective. The specific management operations of messageQueue are not detailed in detail, but the message object recycling is briefly mentioned. The specific details are available and then supplemented.

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.