Android 之 Handler

Android 的消息机制是基于Handler实现的。Handler 采用的是一种生产者-消费者模型,Handler 就是生产者,通过它可以生产需要执行的任务。而 Looper 则是消费者,不断从 MessageQueue 中取出Message 对这些消息进行消费。
MesageQueue是在底层调用native方法去创建的,本质是一个单链表;
一个线程只能有一个Looper,对应一个MessageQueue。

RxBus、EventBus、Rxjava 中跨线程(子线程到主线程)通信都是通过Handler进行的

消息机制的原理

由Message类 、Handler类、MessageQueue类 、Looper类 四个类相互之间协同完成的。
Handler 通过静态方法obtain() 获得 message,然后Handler把message 发送给(send)MessageQueue(消息队列:有插队机制),根据延时的大小,进行排序或插入,looper循环器(自我唤醒机制,当没有消息时或延时没到时 处于阻塞状态,该阻塞状态不同于多线程中的)去MessageQueue中取,Handler 调用,在操作,操作完成之后,通过clean() 方法(洗的是:what 的标识,数据…),再放进消息池。

在这里插入图片描述
生产者消费者模式
在这里插入图片描述

基本用法

        Handler handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                // to do something
            }
        };
        handler.sendEmptyMessage(0);
        //handler.post(runnable);

发送消息

消息的发送主要有post 及 sendMessage,它们都有对应的 delay 方法。

        handler.sendEmptyMessage(0);
        handler.post(runnable);
   public final boolean sendEmptyMessage(int what) {
        return sendEmptyMessageDelayed(what, 0);
    }
    public final boolean post(@NonNull Runnable r) {
        return sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

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

在 sendMessageAtTime 中,它首先通过 mQueue 拿到了对应的MessageQueue 对象,然后调用了 enqueueMessage 方法将 Message 发送至MessageQueue 中

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                                   long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

将 Message 的 target 设置为了当前的 Handler,同时要注意看到,这里在 enqueueMessage 之前先判断了一下 mAsynchronous是否为 true,若为 true 则将该 Message 的 Asynchronous 置为 true

那这个 mAsynchronous 是什么时候被赋值的呢?点进去看可以发现它的赋值是在 Handler 的构造函数中进行的。也就是说创建的 Handler 时若将 async置为 true 则该 Handler 发出的 Message 都会被设为 Async,也就是异步消息

  public Handler(Callback callback, boolean async)
  public Handler(Looper looper, Callback callback, boolean async)

的风格

不论是 post 还是 sendMessage,都会调用到 sendMessag。
sendEmptyMessage(int)
 -> sendEmptyMessageDelayed(int,int)
	 -> sendMessageAtTime(Message,long)
		  -> enqueueMessage(MessageQueue,Message,long)
			-> queue.enqueueMessage(Message, long);

消息的处理

消息的处理是通过 Handler 的 dispatchMessage 实现的:

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

它优先调用了 Message 的 callback,若没有 callback 则会调用 Handler 中Callback 的 handleMessage 方法,若其仍没定义则最终会调用到 Handler 自身所实现的 handleMessage 方法。

Message

消息的创建 通过obtain获取,还可通过new Message()方法

private static final int MAX_POOL_SIZE = 50;


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

    /**
     * Same as {@link #obtain()}, but copies the values of an existing
     * message (including its target) into the new one.
     * @param orig Original message to copy.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        m.workSourceUid = orig.workSourceUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }

    /**
     * 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;
    }

    /**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
     * <em>what</em> members on the Message.
     * @param h  Value to assign to the <em>target</em> member.
     * @param what  Value to assign to the <em>what</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
     * members.
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param obj  The <em>object</em> method to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
     * <em>arg1</em>, and <em>arg2</em> members.
     *
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
     * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
     *
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @param obj  The <em>obj</em> value to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }

消息的清理 主要是recycleUnchecked的方法的处理。


    /**
     * Return a Message instance to the global pool.
     * <p>
     * You MUST NOT touch the Message after calling this function because it has
     * effectively been freed.  It is an error to recycle a message that is currently
     * enqueued or that is in the process of being delivered to a Handler.
     * </p>
     */
    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    @UnsupportedAppUsage
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

MessageQueue

private native static long nativeInit(); //初始化消息队列
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/   //消息的循环与阻塞
private native static void nativeWake(long ptr);//消息的分送与唤醒 

它们分别对应了 Native 消息队列中的 初始化消息队列、 消息的循环与阻塞 以及 消息的分送与唤醒 这三大环节

MessageQueue 实际上是通过一个单链表的数据结构来维护消息列表,单链表在删除和插入上比较有优势。

MessageQueue中存储的Message是没有数量上限的。

消息入队列(排序)enqueueMessage ,通过消息的等待时间进行入队列。

首先在Handler中enqueueMessage()

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                                   long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

到MessageQueue的enqueueMessage()

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            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;
           // 1.取出当前的消息,如果消息等待时间为0或者小于当前消息的等待时间,直接插到当前消息的前面       
           // 2.如果不是上面的情况:循环遍历已经存在的消息,插入到等待时间 大于前一个并且 小于后一个的位置,排队。
            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) {
                        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;
    }

Looper

消息出队列

loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null,
loop 会调用MessageQueue的next方法来获取最新消息,而next 是一个阻塞操作,当没有消息时,next会一直阻塞在那里,这也导致loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,looper就会处理这条消息(msg.target.dispatchMessage(msg),其中msg.target.是发送该消息的handler对象)。
Handler 的dispatchMessage 方法是在创建Handler时所使用的Looper中执行,这样就成功地将代码逻辑切换到指定线程中去执行了

  /**
     * 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;
            }
        }
    }

调用loopOnce方法
mQueue.next(); //调用的是MessageQueue中的 next() 方法

 /**
     * 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;
    }

MessageQueue的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();
            }
			
			//与 Handler 的阻塞唤醒机制有关
            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;
                //判断当前的消息是否是target 为 null 的消息,若 target 为 null,则它会不断地向下取 Message,直到遇到一个异步的消息
                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) {
                //判断当前消息是否到了应该发送的时间,若到了应该发送的时间,就会将该消息取出并返回,否则仅仅是将 nextPollTimeoutMillis 置为了剩余的时间(这里为了防止 int 越界做了防越界处理)
                    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.
                //第一次循环的前提下,若 MessageQueue 为空或者消息未来才会执行,则会尝试去执行一些 idleHandler,并在执行后将pendingIdleHandlerCount 置为 0 避免下次再次执行。
                //若这一次拿到的消息不是现在该执行的,那么会再次调用到 nativePollOnce,并且此次的 nextPollTimeoutMillis 不再为 0 了,这与阻塞唤醒机制有关。
                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. 将空闲处理程序计数重置为0,这样我们就不会再次运行它们。

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

MessageQueue next() 方法,中的nativePollOnce本地方法,此处发生了阻塞,(nativePollOnce消息的循环与阻塞)

与阻塞对应的是唤醒机制在入队的时候enqueueMessage方法。

如何获取主线程Looper

Looper 提供了getMainLooper方法,通过它可以在任何地方获取到主线程的Looper.

Looper 如何停止

Looper 提供了quit和quitSafely

quit和quitSafely区别?

  • quit 会直接退出Looper.
  • quitSafely 是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全的退出。

同步屏障机制

Handler 中存在着一种叫做同步屏障的机制,它可以实现异步消息优先执行的功能。

作用

当MessageQueue的next中遇到了一个同步屏障,则它会不断地忽略后面的同步消息直到遇到一个异步的消息,目的是为了使得当队列中遇到同步屏障时,则会使得异步的消息优先执行,这样就可以使得一些消息优先执行。

加入同步屏障

Handler 中还存在一种特殊的消息,它的 target 为 null,并不会被消费,仅是作为一个标识处于 MessageQueue 中。它就是 SyncBarrier (同步屏障),这种特殊的消息。我们可以通过 MessageQueue的postSyncBarrier 方法将其加入消息队列

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

移除同步屏障

   /**
     * 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
     */
    @UnsupportedAppUsage
    @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;
            //找到 target 为 null 且 token 相同的
            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);
            }
        }
    }

一般执行完了异步消息后就会通过该方法将同步屏障移除。

问题

  1. handler 造成内存泄漏
    Handler 允许发送延时消息,在延时期间用户关闭了 Activity,该 Activity 会泄露。是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。
    方法是:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
    举个例子:
    都知道 匿名内部类会持有外部类的引用,也就是说这里的 Handler 会持有其外部类 XXActivity 的引用。而我们可以回忆一下 sendMessage 的过程中,它会将 Message 的 target 设置为 Handler,也就是说明这个 Message 持有了 mHandler 的引用。那么我们假设通过 mHandler 发送了一个 2 分钟后的延时消息,在两分钟还没到的时候,我们关闭了界面。按道理来说此时 Activity可以被 GC 回收,但由于此时 Message 还处于 MessageQueue 中,MessageQueue 这个对象持有了 Message 的引用,Message 又持有了我们的Handler 的引用,同时由于 Handler 又持有了其外部类 XXActivity 的引用。这就导致此时 XXActivity 仍然是可达的,因此导致 XXActivity 无法被 GC回收,这就造成了内存泄漏
    代码如下:
private static class MyHandler extends Handler {

    private WeakReference<Activity> ref;

    public MyHandler(Activity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        Activity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}

当外部类结束生命周期时,清空 Handler 内消息队列

  @Override
    protected void onDestroy() {
        if (myHandler!= null) {
            myHandler.removeCallbacksAndMessages(null);
        }
        super.onDestroy();
    }
  1. 为什么我们在主线程中直接使用 Handler,而不需要创建 Looper ?
 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.prepareMainLooper(); 代码如下:

 /**
     * 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);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

然后调用 prepare方法,至此创建looper

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        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));
    }
  1. 子线程里弹 Toast 的正确姿势。在子线程里直接去弹 Toast 的时候,会 crash :
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

本质上是因为 Toast 的实现依赖于 Handler。
正确示例代码如下:

new Thread(new Runnable() {
  @Override
  public void run() {
    Looper.prepare();
    Toast.makeText(HandlerActivity.this, "不会崩溃啦!", Toast.LENGTH_SHORT).show();
    Looper.loop();
  }
}).start();

  1. 在非主线程中直接new Handler() 会报如下的错误:
	E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
	E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called 			    Looper.prepare()

原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。

class MyThread extends Thread {
      public Handler mHandler;
     
      public void run() {
          Looper.prepare();
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // to do something
              }
          };
         
          Looper.loop();
      }
}
  1. Looper的创建?一个线程只能有一个Looper?

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

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        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));
    }

prepare的实质:

  • 创建Looper 对象
  • 将创建好的Looper对象存储到ThreadLocal对象里
    注:ThreadLocal对象在创建Looper的时候创建的

ThreadLocal 存储Looper 的处理方式,是保证ThreadLocal里没有其他的Looper,只允许有一份Looper.
这就是为什么同一个Thread线程里只能有一个Looper对象的原因

ThreadLocal 使用场景
  • 一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候
  • 复杂逻辑下的对象传递,比如监听器的传递,有些时候在一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,可以让让监听器作为线程内的全局对象而存在,在线程内部只要通过get方法就可以获取到监听器
  1. 在子线程中更新UI

     在访问UI的时候,ViewRoot会去检查当前是哪个线程访问的UI,如果不是主线程,就会抛出异常:Only the original thread that created a view hierarchy can touch its views。
    

因为Android 的UI控件不少线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。

其中mThread是主线程(UI线程或者MainThread线程),在应用程序启动的时候,就已经被初始化了。

   void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

在 requestLayout 也调用了checkThread方法。

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

在Activity的onCreate方法中创建子线程更新UI不抛异常呢?
执行onCreate方法的那个时候ViewRootImpl对象还没创建,无法去检查当前线程。

  1. IdleHandler 有什么用?
    IdleHandler 是 Handler 提供的一种在消息队列空闲时,执行任务的时机;
    当 MessageQueue 当前没有立即需要处理的消息时,会执行 IdleHandler;
  2. MessageQueue 提供了 add/remove IdleHandler 的方法,是否需要成对使用?
    不是必须;IdleHandler.queueIdle() 的返回值,可以移除加入 MessageQueue 的 IdleHandler;
    9.当 mIdleHanders 一直不为空时,为什么不会进入死循环?
    只有在 pendingIdleHandlerCount 为 -1 时,才会尝试执行 mIdleHander;pendingIdlehanderCount 在 next() 中初始时为 -1,执行一遍后被置为 0,所以不会重复执行;
  3. 是否可以将一些不重要的启动服务,搬移到 IdleHandler 中去处理?
    不建议;IdleHandler 的处理时机不可控,如果 MessageQueue 一直有待处理的消息,那么 IdleHander 的执行时机会很靠后;
    11.IdleHandler 的 queueIdle() 运行在那个线程?
    陷阱问题,queueIdle() 运行的线程,只和当前 MessageQueue 的 Looper 所在的线程有关;
    子线程一样可以构造 Looper,并添加 IdleHandler;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值