标签搜索

Handler机制再解

Pop.Kite
2023-09-11 / 1 评论 / 88 阅读 / 正在检测是否收录...

Handler是Android中的一种消息处理机制,能够将多任务按时间或优先级次序进行处理。由于UI更新并非线程安全的,因此Handler通常会用作UI更新的相关操作。

核心

  • Message
  • Looper
  • MessageQueue

Message

Message是用作消息传递的载体,通常使用Message.obtain()从Message池中获取实例。Message池的容量是50。当一个Message处理完毕后,会调用recycleUnChecked()方法,重置此Message的内容,此时若Message池的容量小于50,则将需要回收的Message指向Message池中。

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

Looper

Looper是Handler的消息轮询器。

prepare()

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

        /**
        * [quitAllowed] 是否允许主动结束轮询
        **/
    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()方法,实际上是向ThreadLocalset了一个Looper。通过代码可知如果当前线程已经set过了Looper,再次调prepare()用会抛出异常Only one Looper may be created per thread

而构造Looper的过程中,Looper也初始化了内部的MessageQueuemThread (用来绑定自己所属的线程)

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

loop()

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

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

通过myLooper()方法,调用ThreadLocal的get方法获取此前set在当前线程的Looper。

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

在for循环中,通过loopOnce()进行消息的轮询器,如果该方法返回False,则退出循环。

/**
 * 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,在此处可能会堵塞
    Message msg = me.mQueue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return false;
    }

      //...
  
      //Looper的观察器
    final Observer observer = sObserver;

      //...
  
      //分发开始的时间
    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 {
          //message的tartget保存了handler,通过dispatchMessage分发消息
        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);
        }
    }

      //...

      //回收Message
    msg.recycleUnchecked();

    return true;
}

以上是Looper轮询机制,当消息队列为空,进入堵塞,但什么时候会唤醒线程呢?

quit()

Looper中的quit方法实际上调用了MessageQueue的quit(),这部分放在MessageQueue中描述。

public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}

MessageQueue

nativeWake()

创建一个Handler,要传入Looper实例,而Looper实例中又存在一个MessageQueue,在调用Handler.enqueueMessage()方法时,实际上是调用了MessageQueue的enqueueMessage方法。

  /**
  * Handler中的sendMessage或Post方法最终调用的都是enqueueMessage
  **/    
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
            //实际调用了MessageQueue中的同名方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }

在MessageQueue中

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

needWake是判断是否需要唤醒Looper线程的方法。此处解答上面Looper阻塞后何时唤醒。

quit()

Looper调用此方法实现退出Looper

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

参数safe决定使用何种退出方式。

//立即回收所有的消息
private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
          //如果消息已经处理过,回收
        if (p.when > now) {
            removeAllMessagesLocked();
        } else {
              //为到期的消息,不进行处理。
            Message n;
            for (;;) {
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

Android层的Handler机制如上。

参考文章

关于handler,看这篇就够了 - 掘金 (juejin.cn)

Android-Handler源码解析-Looper - 掘金 (juejin.cn)

Android组件系列:再谈Handler机制(Native篇) - 掘金 (juejin.cn)

1

评论 (1)

取消
  1. 头像
    LL
    Windows 10 · Google Chrome

    画图

    回复