Android 主线程原理

原文链接 https://www.treeroot.tech/archives/Android%20主线程源码解析

Handler、MainThread、Looper、MessageQueue

主线程

在应用程序开发中我们都知道有个主线程的概念,但其实线程不分主次,只因为它是应用程序进程的第一个线程并且拥有一个阻塞队列,是一个执不会退出的线程,应用程序为了跟用户可以实时交互我们不能启动一个程序然后run方法执行完就退出了,应用程序必须保持可以随时和用户进行交互。
  从应用这一侧来说整个应用程序是从ActivityThread的main方法开始执行的。下面是ActivityThread的main方法:

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

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

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

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

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

Looper 和 MessageQueue

main方法里会调用Looper.prepareMainLooper()来准备主线程Looper,做一系列初始化的工作后再调用Looper.loop(),再看一下loop方法做了什么。下面是Looper的loop方法:

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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;

        // 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();

        for (;;) {
            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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            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.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

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

这里首先从线程局部变量取出当前线程也就是主线程的的Looper对象,然后在拿到Looper内部的MessageQueue对象,再启动一个死循环再调用MessageQueue.next方法获取一个Message对象(这里得死循环绝对不能理解为不停的循环不断next检查是否有消息,这里的循环是没有消息就阻塞的,这里可以暂时理解为next内部会wait当前线程,当然实际机制并不是wait,稍后我会一些解释)。当队列中有消息的时候next方法就会返回,获取一个Message。
  msg.target.dispatchMessage(msg)其实这里的taget是一个Handler对象,大家知道让主线程执行一个任务必须使用Handler来sendMessage,其实post(Runnable)方法本质上也是在sendMessage,下面是Handler的dispatchMessage方法:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

第一个if msg.callback其实是我们在使用post(Runnable)时Handler内部帮我们封装成了Message对象,而把Runnable对象赋值给了Message的callback,所以这里就是执行run方法,第二个if mCallback很少用,这里大家可以自己查看一下代码其实很好理解,handleMessage(msg)就是我们覆写Handler的handleMessage,这样一次消息的流程就执行完了。

MessageQueue.next的阻塞和唤醒

  下面是next方法的源码:
Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        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;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

这个方法内容有点多,但是关键的只有几行,可以看到这里又有一个死循环,内部有nativePollOnce方法接收两个参数,这个方法是一个native方法内部使用c++实现,这个会产生阻塞效果,而第二个参数是阻塞超时时间,这个方法内部的阻塞实际使用的Linux系统的管道流机制如果想了解具体内容可以参考老罗的博客老罗的Android应用程序消息处理机制(Looper、Handler)分析这里对native层的实现有很好的解说。
  在使用Handler.sendMessage的时候Handler内部都会对msg给出一个绝对的执行时间就是msg的一个成员变量when,sendMessageDeley我们会传一个延时时间在Handler内部会把这个时间转换成绝对时间,绝对执行时间就保存在when变量里,而MessageQueue在内部的mMessages变量这个其实是队列的Header Message,而这个队列使用的是链表的数据结构,每一个Message有一个next成员变量可以引用下一个Message对象,从源码看这里首先会拿到mMessages,如果当前时间小于msg的when则说明是不需要立即执行的Message,nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);nextPollTimeoutMillis这个变量是nativePollOnce方法的等待超时时间,这里先不考虑mPendingIdleHandlers的代码,则一次循环结束了,在次调用nativePollOnce方法传入nextPollTimeoutMillis超时时间。如果当前时间大于等于msg的when则说明是需要立即执行的msg则将msg从队列的头移除并返回,就回到了上一个Loop方法调用的next方法获取到了一个msg对象。
  这里还要说一个下如果一个主线程被nativePollOnce阻塞了如果没有超时的Message在未来执行,主线程自己是不能唤醒自己的,所以主线程被唤醒有两种可能一种是当前应用有自己的子线程比如网络请求,子线程回来的时候可以向主线程发消息来唤醒主线程,具体是sendMessage会调用到MessageQueue的enqueueMessage方法在这个方法内部会调用nativeWake来唤醒主线程,另外一种是用户点击屏幕,最终会在WindowServiceManager这个系统级的服务中产生一些事件,然后再把事件传给应用程序一侧来唤醒主线程。

参考:
Android应用程序消息处理机制(Looper、Handler)分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值