Handler/Looper/MessageQueue

420 阅读9分钟

Android 应用消息循环模型

本文基于Android 11 源码介绍下应用消息循环模型,我们带着下面几个问题开始:

  • 一个线程有几个 Handler?
  • 一个线程有几个 Looper?如何保证?
  • 为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
  • 子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
  • 既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
  • Looper死循环为什么不会导致应用卡死?
  • 什么是同步消息屏障?原理是什么样的?

Handler机制相关类

  • Handler:发送和接收消息
  • Looper:用于轮询消息队列,一个线程只有一个
  • Message:消息实体
  • MessageQueue:消息队列,用于消息存储和消息管理
  • Message 类对象中包含一个handler,就是该message对应的target
  • Handler 类对象中包含一个MessageQueue和Looper对象,分别是Handler所在线程的MessageQueue和Looper
  • Looper 类对象中包含一个MessageQueue对象,Looper对象创建的时候New的,并且和Looper进行了绑定

可以看下这个丑陋的类图:

handlerloopermessage.png

Looper

创建Looper

创建Looper是调用Looper.prepare()方法:

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

应用的主线程中系统给我们创建了Looper,所以主线程的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();
    
    //省略部分代码

Looper.prepareMainLooper(),注意这里主线程的Looper是不可以退出的,这是和子线程的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);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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通常调用的是下面的perpare方法:

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

这里如何保证一个线程只存在一个Looper的 ,通过ThreadLocal来保证。

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


  //创建Looper时,会调用set方法,这里保证了线程和Looper的一一对应关系
    sThreadLocal.set(new Looper(quitAllowed));
    
  /**
     * 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();
    }

创建MessageQueue

创建MessageQueue以及Looper和当前线程绑定

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

Looper.loop()方法我们就不详细介绍了,我们可以自定义自己的超时打印,在loop方法中。

Handler

创建Handler

常见的handler创建方法:

        Handler hd = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

看下构造方法:

public Handler() {
    this(null, 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(); // 获取looper
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 绑定messagequeue
    mCallback = callback;
    mAsynchronous = async;
}

Message

创建Message

Message 我们可以使用new的方式,但是更科学的方式是用Message.obtain(),因为这样可以检查是否有可复用的message:

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

Message和Handler绑定

  • 创建Message时可以使用Message.obtain(Handler h)这个构造方法绑定。
  • 也可以在Handler的enqueueMessage方法中绑定
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler 发送消息

  1. Handler发送消息调用sendMessage方法,最终会调用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;
        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;
}
  1. 最终Looper将消息取出,派发给对应的target(Handler)处理,我们可以看到,如果callback 不为空,则直接执行callback方法,否则最后调用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);
    }
}

总结

我们大概总结下,消息的入队和发送,可以看下面的时序图

handler_message_looper.png

同步消息屏障

从上面的代码中我们可以看到,消息队列是按照时间先后顺序去取消息,如果现在有一个需要立刻马上处理的消息怎么办呢? Android引入了同步消息屏障。 那么什么叫同步消息屏障呢? 所谓的同步消息屏障,就是阻碍同步消息,只让异步消息被取出来。 那么如何开启同步消息屏障呢:

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

移除同步消息屏障:

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

同步消息屏障的应用

那么,同步屏障在系统源码中有哪些使用场景呢? Android 系统中的 UI 更新相关的消息即为异步消息,需要优先处理。 比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了 ViewRootImpl#scheduleTraversals() ,如下:

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

最终会调用到下面这个函数:

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }

    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true); // 异步消息
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

HandlerThread

为了便于子线程创建Looper和保证线程安全,于是便有了HandlerThread类, 我们看下代码:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;
    /*
    省略一万行代码
    */

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
}

它的优点就在于它的多线程操作,可以帮我们保证使用Thread的handler时一定是安全的。

扩展,关于epoll_wait

epoll_wait 等待不会消耗CPU资源,会让出CPU资源,等有事件到来时,被唤醒调度。可以查看内核手册关于epoll_wait的说明。

我们常见的主线程的trace :

"main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x74c41f70 self=0xf0272000
  | sysTid=25129 nice=-10 cgrp=default sched=0/0 handle=0xf3f2d494
  | state=S schedstat=( 3432651129 811881287 4651 ) utm=302 stm=41 core=7 HZ=100
  | stack=0xff49f000-0xff4a1000 stackSize=8MB
  | held mutexes=
  kernel: __switch_to+0xc0/0xcc
  kernel: SyS_epoll_wait+0x270/0x384
  kernel: compat_SyS_epoll_pwait+0xa0/0x16c
  kernel: el0_svc_naked+0x24/0x28
  native: #00 pc 00053730  /system/lib/libc.so (__epoll_pwait+20)
  native: #01 pc 00025ec9  /system/lib/libc.so (epoll_wait+16)
  native: #02 pc 0000f035  /system/lib/libutils.so (android::Looper::pollInner(int)+116)
  native: #03 pc 0000ef43  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+26)
  native: #04 pc 000b7e0f  /system/lib/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long long, int)+24)
  at android.os.MessageQueue.nativePollOnce(Native method)
  at android.os.MessageQueue.next(MessageQueue.java:326)
  at android.os.Looper.loop(Looper.java:160)
  at android.app.ActivityThread.main(ActivityThread.java:6747)
  at java.lang.reflect.Method.invoke(Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:915)

在Android系统中有很多应用的地方,比如Input输入机制,InputReader线程中使用getEvent方法监听驱动上报的输入事件:

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

       /*
       省略一万行代码
       */
       int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    }

    return event - buffer;
    }

我们直接看在内核中的实现,关键点在于set_current_state(TASK_INTERRUPTIBLE),

static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
		   int maxevents, long timeout)
{
    /*
    省略一万行代码
    */
    	if (!ep_events_available(ep)) {
		/*
		 * Busy poll timed out.  Drop NAPI ID for now, we can add
		 * it back in when we have moved a socket with a valid NAPI
		 * ID onto the ready list.
		 */
		ep_reset_busy_poll_napi_id(ep);

		/*
		 * We don't have any available event to return to the caller.
		 * We need to sleep here, and we will be wake up by
		 * ep_poll_callback() when events will become available.
		 */
		init_waitqueue_entry(&wait, current);
		__add_wait_queue_exclusive(&ep->wq, &wait);

		for (;;) {
			/*
			 * We don't want to sleep if the ep_poll_callback() sends us
			 * a wakeup in between. That's why we set the task state
			 * to TASK_INTERRUPTIBLE before doing the checks.
			 */
			set_current_state(TASK_INTERRUPTIBLE);
			/*
			 * Always short-circuit for fatal signals to allow
			 * threads to make a timely exit without the chance of
			 * finding more events available and fetching
			 * repeatedly.
			 */
			if (fatal_signal_pending(current)) {
				res = -EINTR;
				break;
			}
			if (ep_events_available(ep) || timed_out)
				break;
			if (signal_pending(current)) {
				res = -EINTR;
				break;
			}

			spin_unlock_irqrestore(&ep->lock, flags);
			if (!freezable_schedule_hrtimeout_range(to, slack,
								HRTIMER_MODE_ABS))
				timed_out = 1;

			spin_lock_irqsave(&ep->lock, flags);
		}

		__remove_wait_queue(&ep->wq, &wait);
		__set_current_state(TASK_RUNNING);
	}
}