Android message mechanism source code reading

foreword

The most common Android client development and message mechanism is that the network requests data to be updated to the UI thread, and the user obtains data through network requests in the sub-thread. Since Android does not support directly modifying UI controls in the sub-thread, the data needs to be transferred to the UI thread. Then update the control interface. This process usually requires the Handler class to call the post/sendMessage series methods to submit the operation to the message queue, and the main thread continuously fetches the messages in the message queue and executes the callback. In order to better understand the entire communication process, here is a look at the underlying implementation source code, starting with the Handler class.

Source code analysis

Handler's post/sendMessage series methods are responsible for putting messages into the message queue to view the implementation source code of these methods.

public final boolean post(Runnable r)
{
   return sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

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

It can be found that all calls will eventually call the sendMessageAtTime() method, in which the Message object is queued into the MessageQueue, that is, the message queue.

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 注意msg.target的赋值操作
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Before looking at the implementation of the message queue, look at the Message class. As an important participant in the Android message mechanism, the message object encapsulates the data submitted by the user and the interface for obtaining the message object from the user.

public final class Message implements Parcelable {
    // 用来指定消息的类型
    public int what;

    // 用户自定义的参数
    public int arg1;
    public int arg2;
    public Object obj;

    // 消息对应的Messenger,这个属性用在Messenger多进程通信上
    public Messenger replyTo;

    // 消息标识,消息是异步消息,是否在使用中
    /*package*/ int flags;
    // 消息发送事件
    /*package*/ long when;
    // Bundle类型的用户数据
    /*package*/ Bundle data;
    // 发送消息的Handler
    /*package*/ Handler target;
    // 消息被接收到后需要执行的回调对象
    /*package*/ Runnable callback;

The above message fields are the fields commonly used in message delivery. Special attention should be paid to the target object of the Handler type, which will be used in the subsequent message queues. Check out the following fields. The linked table mechanism is used to cache the message object.

// 下一个消息引用
/*package*/ Message next;

// 缓存消息对象列表同步器
private static final Object sPoolSync = new Object();
// 缓存消息头部
private static Message sPool;
// 缓存消息数量
private static int sPoolSize = 0;
// 最多缓存消息个数
private static final int MAX_POOL_SIZE = 50;

// 获取缓存消息接口,如果没有缓存就new个新对象
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();
}

// 回收被使用的消息对象到消息缓存池中
public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

The above code is FlyWeight's design pattern. Message objects need to be created and destroyed frequently in Android applications, which will result in constant gc operations. In order to avoid unnecessary creation and destruction operations, the Message is cached in the memory. When the user needs to use it, first determine whether there is a cache in the memory, if there is no new creation, and reuse the previously recycled Message object.

Next, discuss the source of MessageQueue, and you will find that the message queue is a member object from the Looper object, which means that the Looper object creates the message queue.

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Look at the implementation source code of the Looper object, and find that the MessageQueue is officially where the Looper object is created. However, the constructor of Looper is actually a private type, so it can only construct itself inside the Looper, and its instances cannot be created elsewhere.

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

Looking at the place where the constructor is called in the Looper code, it is found that there are three main methods to construct the Looper object, and these Loopers are also stored in the sTreadLocal thread local quantity object.

// 存放Looper的线程本地量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

// 创建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));
}

// 创建主线程也就是UI线程的Looper并且放到线程本地量和sMainLooper中
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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

The above creation method prepared can be called either in the main thread or in the child thread. If prepare() is called twice in the same thread, because there is already a Looper object in the thread local quantity, it will be checked after checking. Throws a runtime exception that can only create one Looper per thread. Where is the looper creation of the main thread sometimes called? There is a main method in Android's ApplicationThread class. Java developers know that the main method is the entry point of a Java program. Check its implementation code.

public static void main(String[] args) {
    ...
    Process.setArgV0("<pre-initialized>");

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

After removing the redundant code, it can be found that the Looper object of the main thread is created when the Andorid application starts, and then the Looper.loop() method is called, and then a runtime exception that the main thread loop exits unexpectedly occurs. If this exception If it is thrown, the Android application also exits, so the loop() method should be an infinite loop operation.

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;

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // 开始死循环
    for (;;) {
        // 从消息队列中取出下一个消息,可以被阻塞
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // 取到消息后调用msg.target.dispatchMessage方法
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        // 回收消息对象到消息缓存中
        msg.recycleUnchecked();
    }
}

msg.target.dispatchMessage(msg); The target of the Handler type was mentioned in the previous Message source code analysis, which is actually a callback of the Handler.dispatchMessage(msg); method, the user usually calls handleMessage() in dispatchMessage(msg); The method will perform different operations for different message types.

The source code of Handler and Looper have been analyzed, and then start to view the MessageQueue implementation that saves the sent Message object. sendMessageAtTime calls the enqueueMessage method to add the message to the message queue.

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

View MessageQueue.enqueueMessage(); The method will normally put the message delivered by the user into the message queue. Note that the operation of enqueuing is performed in the synchronized(this); code block. At this time, because the enqueuing operation can be in a sub-thread, for example, the thread that requests data from the network mentioned above can enqueue the operation to the main thread message queue.

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    sy {
        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 {
            // 
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.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;
}

After joining the queue here, the previous Looper infinite loop will read new messages in the queue in the queue.next(); operation.

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

        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) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
        ....
    }
}

The synchronized(this); code block is also used when the message is dequeued. This is because the underlying singly linked list message queue is asked in multiple threads, and synchronization processing is required, otherwise an exception will occur in multi-threaded access.

Summarize

The ActivityThread.main() method is used as the entry point of the Android application. It calls Looper.preparedMainLooper() to create the main thread Looper, and at the same time calls Looper.loop() to read the messages in the MessageQueue message queue in an infinite loop; the user passes Handler. post/send sends a message to the MessageQueue queue. When Looper detects that a new message has entered the queue, it will return the message from the MessageQueue.next() method and execute msg.target.dispatchMessage() in the main thread; finally call the user to automatically The defined Handler.handlerMessage() method logic, that is, the communication between the child thread and the main thread is realized through the message mechanism.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324602276&siteId=291194637