Handler学习

242 阅读5分钟

代码基于Android SDK 29

Handler基本原理

image.png

子线程使用Handler:首先执行Looper.prepare和Looper.loop。Handler类:

public Handler(@Nullable Callback callback, boolean async) {
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
}

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

在构造Handler时,对Looper进行判断,如果通过ThreadLocal获取的Looper为空,则报错。Looper类:

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper.prepare创建Looper并设置给ThreadLocal,每个Thread只能有一个Looper,否则异常。Looper.loop开始读取MessageQueue中消息。主线程中ActivityThread.main中调用了这两个方法。

MessageQueue.next()来获取消息,没有消息则阻塞在这里。Looper类:

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;
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
    }
}

MessageQueue类:

@UnsupportedAppUsage
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);
    }
}

在native侧,最终使用epoll_wait(native中的源码)来进行等待(Linux中epoll机制)。java中的wait/notify也能实现阻塞等待消息功能,Android2.2及以前,就是这样实现的。 www.androidos.net.cn/android/2.1… 后来用select实现android.googlesource.com/platform/fr… 后来用epoll,是由于native侧的事件,只能使用java的wait/notify就不够用。android.googlesource.com/platform/fr…

一个线程只能对应一个Looper对应一个MessageQueue 对应多个Handler。 多个线程给MessageQueue发消息通过加锁保证线程安全。

boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        ...
    }
}

Handler发送延迟消息:postDelayed-->SendMessageDelayed-->sendMessageAtTime-->enqueueMessage Handler类:

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

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
    return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue类:

boolean enqueueMessage(Message msg, long when) {
    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;
        // 根据 when 进行顺序排序,将消息插入
        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;

}

Handler进行post消息时,在MessageQueue中根据when时长进行顺序排序。

@UnsupportedAppUsage
Message next() {
    for (;;) {
        // 通过epoll_wait等待消息,等待nextPollTimeoutMillis时长
        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;
    }
}
  1. 将传入的延迟时间转化为距离开机时间的毫秒数
  2. MessageQueue根据上一步转化时间进行顺序排序
  3. MessageQueue.next获取消息,对比当前时间(noew)和第一步转化的时间(when),如果now<when,则通过epoll_wait的timeout进行等待。
  4. 如果消息需要等待,进行idel handlers执行,执行完以后再去检查此消息是否可以执行

View.post:

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }

    // Postpone the runnable until we know on which thread it needs to run.
    // Assume that the runnable will be successfully placed after attach.
    getRunQueue().post(action);
    return true;
}

如果attachInfo不为空则通过attachInfo的handler执行;如果handler为空,则通过runQueue执行。 ViewRootImpl类:

final ViewRootHandler mHandler = new ViewRootHandler();
public ViewRootImpl(Context context, Display display) {
    mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
            context);
}
private void performTraversals() {
    final View host = mView;
    if (mFirst) {
        host.dispatchAttachedToWindow(mAttachInfo, 0);
    }
}

ViewRootImpl构造函数,创建了mAttachInfo,然后再performTraversals中,如果mFirst为true,则调用host.dispatchAttachedToWindow,host就是DcorView。mAttachInfo的mHandler是ViewRootImpl内部的ViewRootHandler。调用到DecorView.dispatchAttachedToWindow,ViewGroup.ispatchAttachedToWindow,依次调用child对应的ispatchAttachedToWindow方法。把AttachInfo传进去,在子View中给mAttachInfo赋值。 ViewGroup类:

@Override
@UnsupportedAppUsage
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
    super.dispatchAttachedToWindow(info, visibility);
    mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

    final int count = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < count; i++) {
        final View child = children[i];
        child.dispatchAttachedToWindow(info,
                combineVisibility(visibility, child.getVisibility()));
    }
    final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
    for (int i = 0; i < transientCount; ++i) {
        View view = mTransientViews.get(i);
        view.dispatchAttachedToWindow(info,
                combineVisibility(visibility, view.getVisibility()));
    }
}

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mAttachInfo = info;
}

attachInfo是ViewRootImpl首次触发performTraversals传进来的,View.post是通过ViewRootImpl内部的Handler处理的。 如果在performTraversals之前或者mAttachInfo置为空以后进行执行,则通过RunQueue处理。HandlerActionQueue类:

public void post(Runnable action) {
    postDelayed(action, 0);
}

public void postDelayed(Runnable action, long delayMillis) {
    final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

    synchronized (this) {
        if (mActions == null) {
            mActions = new HandlerAction[4];
        }
        mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
        mCount++;
    }
}

public void executeActions(Handler handler) {
    synchronized (this) {
        final HandlerAction[] actions = mActions;
        for (int i = 0, count = mCount; i < count; i++) {
            final HandlerAction handlerAction = actions[i];
            handler.postDelayed(handlerAction.action, handlerAction.delay);
        }

        mActions = null;
        mCount = 0;
    }
}

getRunQueue().post(action)是将任务放到mAction保存,然后executeActions的时候进行执行。executeActions时机只有一个,dispatchAttachedToWindow里调用的。 View类:

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    mAttachInfo = info;
    if (mRunQueue != null) {
        mRunQueue.executeActions(info.mHandler);
        mRunQueue = null;
    }
}

View.post和Handler.post区别:

  1. 如果performTraversals前调用View.post,则将消息保存,之后dispatchAttachedToWindow时通过ViewRootImpl的handler进行调用。
  2. 如果在performTraversals后调用View.post,则直接通过ViewRootImpl的handler进行调用

View.post的Runnable执行的时候,已经执行过performTraversals,View的measure layout draw 方法都执行过,所以可以获取View的宽高信息。

我们进行UI操作时候,都会调用到ViewRootImpl里。例如requestLayout ViewRootImpl类:

public ViewRootImpl(Context context, Display display) {
    mThread = Thread.currentThread();
}

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

其实检查的是当前线程(一般在主线程创建的)。mThread 指的是 ViewRootImpl 创建的线程。因为ViewRootImpl 创建是在主线程创建的,所以在非主线程操作 UI 过不了这里的检查。