Android-Handler message mechanism implementation principle

First, the message mechanism Process Description

In the application startup, entry function main (), main () will execute the program which will create a Looper object, and then open a infinite loop by this Looper objects, the work of this cycle is continuously removed from the message queue MessageQueue inside That message message object, and processing. Then look at the following two questions:
After a loop to get the message, how to deal with?
By dispatchMessage () method is called Handler in circulation Looper's to deal with, and dispatchMessage () method which calls handleMessage () method, handleMessage () method is usually overridden when using the Handler, so in the end how to deal with the news the developers decided to use Handler.
MessageQueue in the news come from?
Handler will use the developer is added to the message by calling the inside MessageQueue sendMessage () method.

The above is a whole process in Android messaging mechanism, but also "in Android Handler, Looper, MessageQueue, Message What is the relationship?" Answers. Status Handler message can be found in the mechanism by the above process, as a class or auxiliary tools exist for use for developers.

There are two questions about this process:

  • Looper is how to Handler can call the method?
  • Handler is how to insert messages into MessageQueue in?

These two issues will give the answer later, following a first-come through source code, analyze the specific details of this process:

Second, the source of the message mechanism analysis

First, main () method ActivityThread.java located inside the class, which is a hidden class, source location: frameworks / base / core / java / android / app / ActivityThread.java

public static void main(String[] args) {
    ......
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper's creation can be accomplished by Looper.prepare (), above prepareMainLooper Code () was created Looper to use the main thread, prepare () method is called nature. After you create Looper you can call Looper.loop () to open the cycle. The main method is very simple, not much to say, look at the following when Looper was created to do what is below Looper's prepare () methods and variables sThreadLocal:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

Very simple, new a Looper, and save new Looper to ThreadLocal inside out. What ThreadLocal that? It is a class for storing data, similar to the HashMap, ArrayList and other collections. It is characteristic of the data stored in the specified thread, then the data fetch can take only the current thread, such as the following code:

ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
private void testMethod() {

    mThreadLocal.set(0);
    Log.d(TAG, "main  mThreadLocal=" + mThreadLocal.get());

    new Thread("Thread1") {
        @Override
        public void run() {
            mThreadLocal.set(1);
            Log.d(TAG, "Thread1  mThreadLocal=" + mThreadLocal.get());
        }
    }.start();

    new Thread("Thread2") {
        @Override
        public void run() {
            mThreadLocal.set(2);
            Log.d(TAG, "Thread1  mThreadLocal=" + mThreadLocal.get());
        }
    }.start();

    Log.d(TAG, "main  mThreadLocal=" + mThreadLocal.get());
}

log output is

main  mThreadLocal=0
Thread1  mThreadLocal=1
Thread2  mThreadLocal=2
main  mThreadLocal=0

We can clearly see by the above example ThreadLocal data access features, can only take the current thread where the stored data, if the data stored in the thread that is not, take out is null. In fact, this effect can be achieved by HashMap <Thread, Object>, considered thread-safe, then use ConcurrentMap <Thread, Object>, but using the Map have some troublesome thing to deal with, such as when a thread end how we delete this thread For a copy of it? If you are using ThreadLocal do not have this worry, ThreadLocal ensure that each thread has maintained an implicit reference to its copy of a thread-local variables, as long as the thread is alive and the ThreadLocal instance is accessible; after the disappearance of the thread, which thread-local instances All copies will be garbage collection (unless other references to these copies exist of). More ThreadLocal explanation reference: ThreadLocal understanding and management of application scenarios Android threads

Well, back to the topic , prepare () when at the same time create Looper Looper to create a ThreadLocal stored by the introduction of ThreadLocal, access Looper objects is very simple, sThreadLocal.get()you can, the source provides a public static method can be found in anywhere access to the main thread main thread Looper (note the method name myLooper (), many places will be used):

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

Looper create finished, the next open loop, loop method of the key code is as follows:

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;

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        msg.recycleUnchecked();
    }
}

The code above, the main object is first acquired Looper thread, then takes Looper message queue final MessageQueue queue = me.mQueue;, then the following is an endless loop, constantly taken from the message queue Message msg = queue.next();, the message is extracted can be seen a Message object, if the message queue there is no news at this line of code will be blocked until the news comes will be awakened. After taking the message through msg.target.dispatchMessage(msg);to process messages, msg.target is a Handler object, so this time on calls to Hander's handleMessage () method we have rewritten the.
msg.target is when it is assigned? To find the answer easily, msg.target is encapsulated in the message inside, be sure to send a message from there started looking to see how the Message is encapsulated. Then begin from Handler's sendMessage (msg) method, as follows:

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

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

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

You can see the final enqueueMessage () method msg.target = this;, put here to send a message handler encapsulates the message. At the same time we can see, in fact, send a message to the MessageQueue inserted inside a message, and then inside the loop Looper can handle the news. Handler inside the message queue is how come it? From the above code can be seen enqueueMessage () inside the queue is coming from sendMessageAtTime, which is mQueue. See mQueue which is then initialized, see Handler configuration 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 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;
}

mQueue initialization is very simple, first get Looper thread where Handler, Looper then removed in mQueue. This is also the reason why it is necessary to use the Handler in the thread has Looper in, you can easily get mQueue of Looper's message queue to insert the news (+ blocked with Looper cycle to achieve the effect of sending and receiving messages).

These are the principles of the main thread message mechanism.

So, the reason to use any thread handler in the following practices, principles, and other internal processes is very clear:

new Thread() {
    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    }
}.start();
  1. First Looper.prepare () Looper create and initialize a message queue MessageQueue Looper held, created Looper will be saved to a ThreadLocal Handler convenient direct access.
  2. Then Looper.loop () open loop taken from the message and calls the handler which MessageQueue dispatchMessage (msg) method for processing a message. If there are no messages MessageQueue, blocking circulation will go to sleep, so when there is news to be awakened to process messages.
  3. And then our new Handler () when, Handler constructor and get Looper Looper got the MessageQueue object. Then the internal Handler which can be inserted directly into the MessageQueue message, and send a message that is inserted into the message, this time there will be a wake-up message to process the message loop Looper. Processing the message is to call dispatchMessage (msg) method, we rewrite the final call to the Handler of handleMessage () method.


Third, strengthen the understanding of the message mechanism by studying a number of issues

Source code analysis is over, look at the following two questions at the beginning of the article:

  • Looper is how to Handler can call the method?
  • Handler is how to insert messages into MessageQueue in?

These two issues source code analysis has been given the answer, here to do some summary, first find out the relationship between objects in the message mechanisms:

Looper,MessageQueue,Message,ThreadLocal,Handler
  1. Looper object has a member MessageQueue, MessageQueue is a message queue for storing messages Message
  2. In a handler with the Message object, the message Looper removed, can be easily Handler call to a method (to solve the problem 1)
  3. Message handler with how objects? When the handler is to send a message to the message in their own package of.
  4. How Handler is sending a message? Looper is thus achieved by obtaining Looper MessageQueue inside the object, then Handler which can be inserted directly into the message of the MessageQueue. (2 problem solving)
  5. Handler is how to get Looper object? Looper in the creation of their own at the same time to save the ThreadLocal and provide a public static method can be removed from the ThreadLocal in Looper, Handler's constructors so you can call a static method to obtain Looper object directly.

With the above series of questions to see the source code is very clear, the following is a know almost Q:

Android why the main thread will not Looper.loop () stuck in the cycle of death?

The reason is simple, there are blocking circulation, so the cycle of death and would not have been executed, on the contrary, most of the time there is no news, so the main thread is dormant most of the time, it does not consume too much CPU resources lead to stuck.

  1. Blocking principle is to use the pipeline mechanism Linux implementation
  2. When the main thread is not blocked in the message processing of the read end of the pipe
  3. binder thread will be added to the main thread message queue message, and then to write a byte write end of the pipe, so you can wake up the main thread returns from the read end of the pipe, that is to say looper cycle in queue.next () calls return .. .

Here comes binder thread, the specific implementation details do not have to get to the bottom, consider the following questions:
How endless loop of the main thread to deal with other matters?
You first need to understand the problem, the main thread to enter an infinite loop Looper, how to deal with other matters, such as the activity of the various life cycle callback function to be executed is how to (note that this is in the code is executed in the order with the next thread if this obstruction in an infinite loop, then the code outside into an infinite loop after loop is how to implement it).
First, look at the source code of the main function

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
}

Looper.loop();

Between Looper.prepare and Looper.loop new one ActivityThread and calls its attach method, which is open binder thread, another new ActivityThread () initializes the time when a member of its type H, H is He inherited a Handler class. At this time, the result is: Before the main line Chengkai Qi loop infinite loop, has been launched binder threads and ready Handler named H, then the next to do some transaction in addition to the main thread infinite loop is very simple, only need to send a message through the binder threads to H can be, for example, a message is sent H.LAUNCH_ACTIVITY notify the main thread calls Activity.onCreate (), of course, is not called directly after H receives the message will conduct a series of complex final call to the function call Activity.onCreate ().
As for who will control the binder thread is not a message to the H-depth study, the following is the "Android development of artistic exploration," which words:

ActivityThread ApplicationThread by inter-process communication and AMS, AMS in the inter-process communication completion request ActivityThread manner will callback method ApplicationThread Binder, and then sends a message to ApplicationThread H, H will receive the message switching logic in ApplicationThread go to ActivityThread executed, i.e. to switch execution to the main thread, the process is the main thread of the message loop model.

The problem here, see more know almost original

At last

And other systems the same, Android applications also rely on message-driven to work. Online this sentence is still very reasonable.


Reference article:

"Android development of artistic exploration"
Android why the main thread will not Looper.loop () stuck in the cycle of death?
Android ThreadLocal thread management's understanding and application scenarios
Android messaging - you really know Handler
Android Handler is what in the end

Guess you like

Origin www.cnblogs.com/developerzjy/p/11084125.html