Handler 梳理


仓库:生产者和消费者共用同一个存储空间,生产者往存储空间中放数据,消费者从存储空间中取数据。

生产者:存储空间满时,生产者线程挂起,当空间不满时「消费者拿走了数据」唤醒生产者线程。

消费者:生产者和消费者互相不持有,当没有数据时,消费者线程挂起,有数据时将消费者线程唤醒。

Handler 的消息处理机制关联类:Handler、MessageQueue、ActivityThread、Looper、ThreadLocal、ThreadLocalMap、Thread、Message

源码分析

Handler native层: epoll机制

Handler:

Handler 中默认的 looper 是Looper.myLooper();
boolean async :true 为异步,false为同步

    public Handler(@Nullable 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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

sendMessageAtTime与sendMessageAtFrontOfQueue都有调用到enqueueMessage方法
Message 添加到 消息队列MessageQueue 中

    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public boolean sendMessageAtTime(@NonNull 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);
    }

    /**
     * Enqueue a message at the front of the message queue, to be processed on
     * the next iteration of the message loop.  You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     * <b>This method is only for use in very special circumstances -- it
     * can easily starve the message queue, cause ordering problems, or have
     * other unexpected side-effects.</b>
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        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, 0);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        // 设置当前Handler
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            // Handler设置异步方法
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

创建异步的Handler调用方法
① 第一种:调用Handler的createAsync方法可创建异步,最终还是走②的方法。
②第二种: 创建Message时设置此方法setAsynchronous(true)亦可创建异步;

    /**
     * Create a new Handler whose posted messages and runnables are not subject to
     * synchronization barriers such as display vsync.
     *
     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
     * but not necessarily with respect to messages from other Handlers.</p>
     *
     * @see #createAsync(Looper, Callback) to create an async Handler with custom message handling.
     *
     * @param looper the Looper that the new Handler should be bound to
     * @return a new async Handler instance
     */
    @NonNull
    public static Handler createAsync(@NonNull Looper looper) {
        if (looper == null) throw new NullPointerException("looper must not be null");
        return new Handler(looper, null, true);
    }

    /**
     * Create a new Handler whose posted messages and runnables are not subject to
     * synchronization barriers such as display vsync.
     *
     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
     * but not necessarily with respect to messages from other Handlers.</p>
     *
     * @see #createAsync(Looper) to create an async Handler without custom message handling.
     *
     * @param looper the Looper that the new Handler should be bound to
     * @return a new async Handler instance
     */
    @NonNull
    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
        if (looper == null) throw new NullPointerException("looper must not be null");
        if (callback == null) throw new NullPointerException("callback must not be null");
        return new Handler(looper, callback, true);
    }

Looper.loop()方法中调用到了Handler的dispatchMessage()方法执行在主方法中的消息回调。
调用出处Looper的loop()

public static void loop() {
        final Looper me = myLooper();
        .......
        final MessageQueue queue = me.mQueue;
        .......
        for (;;) {
            Message msg = queue.next(); // might block
            .......
            try {
                // Handler handler = msg.target; 调用Handler的dispatchMessage( Message msg) 方法
                msg.target.dispatchMessage(msg);
                 .......
            } catch (Exception exception) {
                 .......
            } finally {
                 .......
            }
             .......
            msg.recycleUnchecked();
        }
    }

Handler的dispatchMessage() 种有三个回调地方
① Message 若有设置callback,则回调 handleCallback(msg)
② Handler 构造方法中的mCallback 若有设置,则回调mCallback.handleMessage(msg),返回true则中断下面的handleMessage回调
③ Handler 内部公共方法handleMessage,如果mCallback为空或mCallback.handleMessage(msg)返回值为false,则会回调handleMessage(msg); 一般可在Handler 类中重写此方法设置监听

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

    private static void handleCallback(Message message) {
        message.callback.run();
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
    }

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

MessageQueue:

消息入队列

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

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

        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;
                //指示next()是否被阻止在pollOnce()中以非零超时等待。如果阻塞,则需要唤醒looper
                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.
               // 插入队列中间。 通常,除非队列的开头有障碍并且消息是队列中最早的 异步消息,否则我们不必唤醒事件队列。(队列中消息不为空,并且next()也没有阻塞)
                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.
          // 如果looper阻塞/休眠中,则唤醒looper循环机制处理消息
            if (needWake) {
                nativeWake(mPtr); // 唤醒
            }
        }
        return true;
    }

消息出队列
Looper.loop()方法中调用到了MessageQueue的next()方法读取消息出队。
当读取消息时发现消息队列中没有消息时(nextPollTimeoutMillis为-1 则为无消息),就会调用nativePollOnce(ptr, nextPollTimeoutMillis);将next()阻塞在PollOnce中。looper循环就进入了休眠期状态。

    @UnsupportedAppUsage
    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();
            }
             // 在这里根据nextPollTimeoutMillis判断是否要阻塞
            // nextPollTimeoutMillis: 0 有消息; -1无消息
            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.
                    // 被障碍挡住了。 在队列中查找下一条异步消息。
                    // msg.isAsynchronous() == false时,此数据是同步消息,继续循环
                    //  msg.isAsynchronous() == true时, 此数据是异步消息,终止循环
                    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.
                    // 队列中没有消息,标记阻塞looper循环进入休眠
                    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;
        }
    }

同步屏障
同步屏障解释:阻碍同步消息,只让异步消息通过,给紧急执行的消息一个绿色屏障,也就是所谓的同步屏障。
特点:Message msg中的msg.target为null; msg.setAsynchronous(true);

1、添加同步屏障

    /**
     * Posts a synchronization barrier to the Looper's message queue.
     *
     * Message processing occurs as usual until the message queue encounters the
     * synchronization barrier that has been posted.  When the barrier is encountered,
     * later synchronous messages in the queue are stalled (prevented from being executed)
     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
     * the token that identifies the synchronization barrier.
     *
     * This method is used to immediately postpone execution of all subsequently posted
     * synchronous messages until a condition is met that releases the barrier.
     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
     * and continue to be processed as usual.
     *
     * This call must be always matched by a call to {@link #removeSyncBarrier} with
     * the same token to ensure that the message queue resumes normal operation.
     * Otherwise the application will probably hang!
     *
     * @return A token that uniquely identifies the barrier.  This token must be
     * passed to {@link #removeSyncBarrier} to release the barrier.
     *
     * @hide
     */
    @TestApi
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

2、移除同步屏障

    /**
     * Removes a synchronization barrier.
     *
     * @param token The synchronization barrier token that was returned by
     * {@link #postSyncBarrier}.
     *
     * @throws IllegalStateException if the barrier was not found.
     *
     * @hide
     */
    @TestApi
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

ActivityThread:

重点方法在 main方法
主要两句代码:
Looper.prepareMainLooper();
Looper.loop();

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

        // Install selective syscall interception
        AndroidOs.install();

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

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

        // Call per-process mainline module initialization.
        initializeMainlineModules();

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

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        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:

重点几个方法
1、prepareMainLooper()
此处注意点ThreadLocal:
① static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
② sThreadLocal.set(new Looper(quitAllowed));
③ sThreadLocal.get();

    // sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    @UnsupportedAppUsage
    private static Looper sMainLooper;  // guarded by Looper.class

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) { // sThreadLocal不为空,则Looper已存,一个线层Looper只能有一个
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed)); // 创建Looper
    } 

   /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. See also: {@link #prepare()}
     *
     * @deprecated The main looper for your application is created by the Android environment,
     *   so you should never need to call this function yourself.
     */
    @Deprecated
    public static void prepareMainLooper() {
        prepare(false); // 主线程的Looper, 不允许退出
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

   /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

2、loop()
重点:此处代码中需要关注的是
① Message 关联代码
Message msg = me.mQueue.next();
从消息队列中取出Message ,以便后续处理
② Handler 关联代码
msg.target.dispatchMessage(msg);
Handler handler = msg.target;
在主线程中使用 Handler回调dispatchMessage(msg)方法,故此Handler 的 callback 是主线程。

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    @SuppressWarnings("AndroidFrameworkBinderIdentity")
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;

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

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        me.mSlowDeliveryDetected = false;

        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

    /**
     * Poll and deliver single message, return true if the outer loop should continue.
     */
    @SuppressWarnings("AndroidFrameworkBinderIdentity")
    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        // 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);
        }
        // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (me.mSlowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    me.mSlowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    me.mSlowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

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

        return true;
    }

3、构造方法Looper(boolean quitAllowed)
两个重点:
① 创建 MessageQueue
② 与当前线程绑定

    @UnsupportedAppUsage
    final MessageQueue mQueue;
    final Thread mThread;

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); // 创建消息队列
        mThread = Thread.currentThread(); // 绑定当前线程
    }

ThreadLocal 与 ThreadLocalMap:

ThreadLocal 需要关注 get 、set 相关方法。
在 Handler 与 Looper 中,ThreadLocal 使用 的ThreadLocalMap ,其中 key 是 ThreadLocal,value 是Looper

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        // 在当前的线程Thread创建ThreadLocalMap
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocalMap 需要注意ThreadLocalMap构造方法,getEntry、set、remove 等方法

        /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

        /**
         * Get the entry associated with key.  This method
         * itself handles only the fast path: a direct hit of existing
         * key. It otherwise relays to getEntryAfterMiss.  This is
         * designed to maximize performance for direct hits, in part
         * by making this method readily inlinable.
         *
         * @param  key the thread local object
         * @return the entry associated with key, or null if no such
         */
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            // Android-changed: Use refersTo()
            if (e != null && e.refersTo(key))
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

       /**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        /**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }


Message:

创建 Message 可以直接 new,但建议使用 Message.obtain(),因为它会检查是否有可以复用的 Message,避免过多的创建和销毁 Message,进而达到优化内存和性能的目的。

    /**
     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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();
    }

Handler 的常见面试题

  1. 一个线程有几个 Handler ?

答:可以有任意个,可以在任何时候 new() 出来。流水线只有一条,但工人可以有很多。

2.一个线程有几个 Looper?如何保证唯一性?

答:每个线程只有一个looper,而每个Thread中都又一个关键ThreadLocalMap,通过ThreadLocal从ThreadLocalMap中取出looper
Threadlocal调用set方法存入Looper时,会通过当前的Thread去取得对应的ThreadLocalMap,然后把Threadlocal为key,Looper为value。
当Threadlocal调用get方法时,通过当前的Thread去取得对应的ThreadLocalMap,传入当前的Threadlocal去取得对应的Looper.

3.Handler 内存泄漏的原因?为什么其他内部类没有出现过这个问题?

答:Message.target 指向具体的 Handler,也就是 Message 持有了Handler。Handler 可以调用 Activity 的方法,表明 Handler 持有 Activity 对象。如果我们设置了一个 20 分钟后执行的 Message,那么在这 20 分钟内,Message 会一直持有 Handler,Handler 会一直持有 Activity ,进而产生内存泄露。

4.为何主线程可以 new Handler()?如果要在子线程中 new Handler()需要怎么做?

答:因为程序启动时,主线程会调用 Looper.prepareMainLooper()。这样主线程中就有了 Looper,就可以 new Handler,前面说过 Looper 就好比流水线,Handler 是工人,流水线都没有,你招工人吃空饷吗?所以子线程想要 new Handler 就先得有流水线 Looper。

Looper.prepare()
Looper.loop() // 流水线开机了才可以让工人工作嘛

4.子线程中维护的 Looper,消息队列中无消息的时候处理方案是什么?有什么用?

队列中无消息的时候,可以调用 Looper.quit() 方法停止 loop()。quit() 方法会清空消息队列中的所有消息,然后传入一个为空的 Message,进而让 loop()停止。

4.Looper 死循环为什么不会导致线程卡死?

ANR 问题的出现是消息超时响应,而 Looper 死循环是取消息的,所以并不是一个问题,偷换了概念。此外,ANR 的触发,也通过 Looper 取用弹出 ANR 消息。

5.handler引起的内存泄露及解决办法。

handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,这样在GC垃圾回收机制进行回收时发现这个Activity居然还有其他引用存在,因而就不会去回收这个Activity,进而导致activity泄。

解决方案:
1.把handler设置成静态内部类,因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露

public class MainActivity extends Activity{
      static class MyHandler extends Handler{
           public void handleMessage(Message msg){
               // 处理消息
           }
      }
}

2.onDestroy生命周期中调用 handler.removeCallbacksAndMessages(null);,进行释放。

public class MainActivity extends Activity{
         public void onDestroy() {
             handler.removeCallbacksAndMessages(null);
         }
      }
}

3.handler内部类持有外部activity的弱引用

public class MainActivity extends Activity{
      class MyHandler extends Handler{
           public MyHandler(MainActivity mainActivity) {
               mWeakReference = new WeakReference<>(mainActivity);
            }
           //持有弱引用MainActivity,GC回收时会被回收掉.
           WeakReference<OkhttpTestActivity> mWeakReference;
           public void handleMessage(Message msg){
               // 取出activity,处理消息
               MainActivity activity = mWeakReference.get();
               // ......
           }
      }
}

1:子线程到主线程通信都有哪些方式?子线程到主线程通信的原理?--阿里
答:不管任何方式切换,最终的底层逻辑还是用到Handler。原理如上。

2: 主线程可以创建多少 Handler?如果想要在子线程中创建 Handler 要做些什么准备?--网易
答:可以创建无数个Handler; 子线程要创建Handler,首先在子线程中使用Looper.prepare(),然后获取Looper.myLooper()传给Handler,接着在执行Looper.loop();

3: MessageQueue中存储的Message数量有上限吗?为什么这么设计?能不能用阻塞队列做MessageQueue --手百
Message无上限,整个App所有的事件都是通过Handler传递的,如果限制数量那就会有丢失事件的传递,且不能阻塞队列。

4: IdleHandler你了解吗?做什么用的 --腾讯
答:IdleHandler 是 Handler 提供的一种在Message队列空闲时,执行任务的时机;MessageQueue中没有立即执行的消息时,会执行IdleHandler,调用queueIdle()方法。

5:我们使用 Message 时应该如何创建它?--阿里
答:调用obtain()方法创建,会从空余的Message队列中取出赋值使用。Message空余队列上限50个。
6:handler没有消息处理是阻塞的还是非阻塞的?为什么不会有ANR产生?--腾讯

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容