preface

In a previous article I wrote about the Android messaging mechanism and how the Handler sends messages. Here’s the link:

Post (Runnable R) to tease out Android’s messaging mechanism (and Handler memory leaks) again

In the message mechanism, there is a very important thing, that is Looper, Looper is mainly used to fetch messages from the message queue to the Handler, but not limited to this, there are a lot of things worth we go to the source code to see:

1. Start with looper.prepare ()

To process messages in a thread, the code looks like this:

class LooperThread extends Thread
{
    public Handler mHandler;
    public void run(a) 
    {
    Looper.prepare();
    mHandler = new Handler() 
    {
    public void handleMessage(Message msg) 
    {
    // process incoming messages here}}; Looper.loop(); }Copy the code

We must first call looper.prepare (). What does this method do:

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

Copy the code

Sthreadlocal. set(new Looper(quitAllowed)

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

ThreadLocal: Represents a thread-local variable. Each thread sees its own value and is not aware that it exists in any other thread.

The purpose of ThreadLocal here is to ensure that each thread has its own Looper

The above judgment also illustrates a problem: a thread can only have one Looper

New Looper(quitAllowed) :

final MessageQueue mQueue;
final Thread mThread;

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

In the constructor, MessageQueue is initialized with the mThread attribute that represents the current thread. For MessageQueue, see the link at the beginning of this article.

Calling looper.prepare () creates a separate Looper for the current thread using ThreadLocal, which contains a message queue

2. Create Handler->new Handler()

After creating a Looper for the current thread, we can create a Handler to handle the message.

How is Handler associated with Looper?

// Global variables
final Looper mLooper;
final MessageQueue mQueue;

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;
    }
Copy the code

There are two global variables in Handler, mLooper and mQueue, that represent the Looper and message queue associated with the current Handler and are initialized in the constructor. Importantly, looper.mylooper () :

public static @Nullable Looper myLooper(a) {
        return sThreadLocal.get();
    }
Copy the code

This method returns null if the current thread has no Looper associated with it.

Note that the Handler is associated with the Looper of the thread in which it was created, or the specified Looper can be passed in the Handler constructor

3.Looper.loop() reads the message

The core of this method, as discussed in the previous article, is an endless loop that fetches messages from MessageQueue to a Handler.

Principle of thread message mechanism

After looking at the source code, we can see why messages need to be processed in a thread. We must go through the above three steps in the same order

Looper.prepare() : Prepares the message queue for the current thread

The Handler default constructor is associated with Looper in the current thread

3.Looper.loop() enables the loop to fetch messages

Derivative problems

How many loopers can a thread have?

This problem was discussed earlier. There can Only be one, otherwise a call to looper.prepare () will throw a runtime exception saying “Only one Looper may be created per thread”.

A thread can have several handlers

You can create an infinite number of handlers, but they all use the same message queue, the same Looper

How does the same Looper differentiate between different handlers, in other words, how do different handlers handle the messages they send

This is where the problem comes in Handler’s sendMessage method, which I’m not going to describe here

Handler.enqueueMessage

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

MSG. Target = this; The current Handler is assigned to the Message object so that MSG. Target can be used to distinguish between different handlers while processing the Message. This is done in looper.loop:

Looper.loop()

. msg.target.dispatchMessage(msg); .Copy the code

By the way, there are also assignments to target within the various overload methods on Message Obtain