Explore Handler source code from sendMessage to handleMessage

Handler

From sendMessage to handleMessage

Follow my rhythm and press Ctrl, click the Handler with the mouse, and start to be happy! Skr! !

First find our most commonly used method sendMessage (message)

It directly calls sendMessageDelayed(message, delayMillis)

However, sendMessageDelayed only does a little verification operation (non-negative verification for delayMillis)

Then it directly calls sendMessageAtTime(message, SystemClock.uptimeMillis() + delayMillis);

sendMessageAtTime: The main event begins

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

It can be seen that starting from this method, MessageQueue (message queue) appears

After creating the message queue, pass in the message and the delay time.

enqueueMessage in Handler:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    
    
    msg.target = this;// Message 注入 handler
    if (mAsynchronous) {
    
    // {true:异步;false:同步}
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue is a priority queue sorted by time

enqueueMessage in MessageQueue:

boolean enqueueMessage(Message msg, long when) {
    
    
        if (msg.target == null) {
    
     // 校验 handler
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
    
    // 判断该消息 是否 被占用
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
    
    
            if (mQuitting) {
    
    
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
    
    
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
    
    
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
    
    
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
    
    //根据 when(也就是时间)来确定 新消息的位置
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
    
    
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg; // 完成链表的变换(完成消息插入操作)
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
    
    
                nativeWake(mPtr);
            }
        }
        return true;
    }

MessageQueue time priority queue new message insertion diagram:

priority queue

The route from sendMessage to MessageQueue has been opened, so let's look at the Looper line.

sendMessage is to add new messages to the message queue, and Looper is to consume (process) messages in a loop.

Looper's loop() method mainly does two things: (First declare that Looper is an infinite loop)

  • Get the message out of the message queue.

    Message msg = queue.next(); // might block ( next()可能阻塞 )
    

    The next() method of MessageQueue is to take out the next message (message)

  • Call the dispatchMessage(msg) method of Handler to execute the message

    msg.target.dispatchMessage(msg);
    

    Handler's dispatchMessage method:

    public void dispatchMessage(Message msg) {
          
          
        if (msg.callback != null) {
          
          
            handleCallback(msg);
        } else {
          
          
            if (mCallback != null) {
          
          
                if (mCallback.handleMessage(msg)) {
          
          
                    return;
                }
            }
            handleMessage(msg);//就是我们重写的方法。
        }
    }
    

    Finally, call the handleMessage(msg) method: thus forming a closed loop. Great! ! !

problem seeking

Where is the Looper initialized?

Looper initialization:

First look at the construction method of Looper: (It is actually private, indicating that it is not a Looper started by other classes)

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

Looper has its own startup class method: prepare(true)

private static void prepare(boolean quitAllowed) {
     
     
    if (sThreadLocal.get() != null) {
     
     
        //如果发现对应的threadLocal(key) 已经 有对应的Looper(value)了,抛出异常。
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

With the Looper, where does the power (thread) come from?

Let's keep exploring! (Click into the ThreadLocal.set() method)

public void set(T value) {
     
     
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);// 获得当前线程的 threadLocalMap(用来保存线程上下文的)
    if (map != null)
        map.set(this, value);// 将此 threadLocal 与 looper 对象绑定到上述 map 中去。
    else
        createMap(t, value);
}

The getMap method is to return the threadLocals member variable of the corresponding thread.

ThreadLocalMap getMap(Thread t) {
     
     
    return t.threadLocals;
}

This completes the binding.

A thread corresponds to a ThreadLocalMap, which stores a pair of ThreadLocal (key): Looper (value) mappings .

Handler will cause memory leaks, why? How to solve it?

When we use Handler, we generally use anonymous inner classes to override the handleMessage() method.

It is the use of this anonymous inner class that causes the memory leak.

So other inner classes can cause memory leaks? Why?

This question is naturally clear later.

Let me first talk about why the internal class of Handler causes memory leaks.

  • From the perspective of Java syntax, a non-static (static) inner class will hold an object (this) of an outer class.

    In this way, the inner class can directly access the member properties of the outer class.

  • From the execution mechanism of the Handler, we can find that the message will be bound to the handler, and then enter the MessageQueue.

  • The Message in the MessageQueue may be a delayed message (delay), and the message may survive for quite a while.

  • During the period of message survival, the this held by the handler cannot be released, even though the page has been destroyed.

  • From the perspective of GC recycling mechanism, objects with strong references will not be recycled easily, and JVM would rather throw an exception.

It can be seen that pure internal classes will not cause memory leaks. It is because of the unique operating mechanism of Handler that this problem is caused.

So how do we solve it?

  • Triggered from the perspective of Java syntax, non-static inner classes will hold outer class objects, but static outer classes will not. We can make the Handler object static.
  • In the analysis, we mentioned that strong references are not easy to recycle. Correspondingly, we can use weak references (WeakReference), so that when necessary, JVM can recycle external class objects (when we want to use external class objects, we can also Go to ReferenceQueue to get it).

Why can we use new Handler directly in the main thread? Did not see the relationship with Looper. Why?

When we use Handler in Activity.

final Handler handler =  new Handler(){
     
     
    @Override
    public void handleMessage(Message msg) {
     
     
        super.handleMessage(msg);
    }
};

From the beginning to the end, we didn't care where the Looper was prepared. We just used it like this. If it can be used, it means that there must be a place to prepare it.

We know that Android is an event-based framework.

If he wants to do something, he must first have some stimulation.

Then the event that stimulates the APP to start running : it should be that we click the APP icon on the desktop .

What exactly is going on in this process.
insert image description here

Let me clarify a few points:

  • Android main interface (desktop) is also an APP, usually called launcher
  • Zygote is the core process of creating a new process in the Android system. It is responsible for starting the Dalvik virtual machine, loading some necessary system resources and system classes, starting the system_server process, and then waiting to process app application requests.
  • Application is an instance of a virtual machine (Dalvik/Art). Each process has only one virtual machine, and each process has only one Application instance. A single-process App has only one Application instance, and a multi-process App has multiple Application instances
  • Each program has its own program entry, and the APP's program entry is the main method in the ActivityThread class.

In fact, I want to draw out so many words. The program entry of the APP is in the ActivityThread, and there must be a secret in it.

Part of the main method of the ActivityThread class:

public static void main(String[] args) {
    
    
    
    //...被忽略的代码段...

    Looper.prepareMainLooper();
    
    //...被忽略的代码段...
    
    if (sMainThreadHandler == null) {
    
    
        // 获得主线程的 handler
        sMainThreadHandler = thread.getHandler();
    }

    //...被忽略的代码段...
    
    Looper.loop();

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

It can be seen that at the entrance of APP, we found the initialization of Looper.

Looper.prepareMainLooper:

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    
    
    prepare(false);// 创建 Looper 对象,并与主线程绑定
    synchronized (Looper.class) {
    
    
        if (sMainLooper != null) {
    
    
            //同样,针对主线程的 Looper 也只准有一个
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

From this we found that, in fact, all of these things should be there somewhere.

And we found the H class by following the vine, (hold down Ctrl and click into the getHandler method)

class H extends Handler

This H inherits the Handler class and rewrites many methods, which shows that the Handler for the main thread is still very different.

Class H: handleMessage method local:
insert image description here

After a closer look, I found out that the situations handled here seem to be very important component actions.

  • In particular, the operation of exiting APP (EXIT_APPLICATION) is actually quitting the Looper of the main thread.

It is possible to operate the exit of the APP, which shows that the processing mechanism of Android is probably the Looper + Handler mode.

If we build a Handler mechanism in the child thread, how should we end it?

why: (otherwise there will be an infinite loop, the thread will never end, and the resource will never be released)

We know that the loop() method of Looper is an infinite loop. When there is no message in MessageQueue, it will block.

Message msg = queue.next(); // might block 

Local code in the MessageQueue.next() loop:

for (;;) {
     
     
    if (nextPollTimeoutMillis != 0) {
     
     
        Binder.flushPendingCommands();
    }

    //当 nextPollTimeoutMillis 为 -1 时,此方法将从 native 层持续阻塞,直到有事件发生
    //如果 nextPollTimeoutMillis 为 0 时,则无需等待,直接返回。
    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
     
     
        // Try to retrieve the next message.  Return if found.
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        if (msg != null && msg.target == null) {
     
     
           //...被忽略的代码段...
        if (msg != null) {
     
     
            //...被忽略的代码段...
        } else {
     
     
            // No more messages. 当没有消息时
            nextPollTimeoutMillis = -1;
        }

        // Process the quit message now that all pending messages have been handled.
        // 处理完所有挂起的消息后,立即处理退出消息。 
        if (mQuitting) {
     
     // 在调用 quit()
            dispose();
            return null;//返回一个 null ,让loop()跳出循环。
        }

        //...被忽略的代码段...
}

Local code inside Looper.loop() loop:

for (;;) {
     
     
    Message msg = queue.next(); // might block 
    if (msg == null) {
     
     //当 next 方法 返回一个 null 时,跳出循环。
        // No message indicates that the message queue is quitting.
        return;
    }
	//...被忽略的代码段...
}

From the above code, we can see that to make loop() jump out of the loop, two steps are required:

  • Wake up the thread and let it continue execution

    nativePollOnce is likely to have blocked the thread and needs to call nativeWake to wake it up.

  • Let mQuitting = true, so that loop() will enter the return branch.

When calling quit, MessageQueue.quit():

void quit(boolean safe) {
     
     
    if (!mQuitAllowed) {
     
     
        //主线程不允许自主 quit
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
     
     
        if (mQuitting) {
     
     
            return;
        }
        //将其设置为 true ,为了next() 方法 返回一个 null,进而让 loop 跳出循环
        mQuitting = true;

        //将 Message对象 的空间都置空。(Message的创建和维护采用了 享元设计模式)
        if (safe) {
     
     
            removeAllFutureMessagesLocked();
        } else {
     
     
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);//将线程唤醒
    }
}

At this point, the loop jumps out of the loop, and this handler action ends here!

Guess you like

Origin blog.csdn.net/weixin_43415201/article/details/119912147