Android 系统系列 Looper & handler 机制 回顾

-

本文为Android 系统相关系列的一部分

刚学android的时候也写过一篇handler机制的文章,现在倒回去看觉得当时不是很懂原理,现在再过一遍

由于读源码时,我们见到的50%到80%都是没有营养的东西,所以我不会一点一点像单步调试一样地写下来,记要点就可以了


Event Loop

  • 应用编程发展到现在这个阶段,事件循环机制基本已经成为绝大多数GUI及相关框架必备的特性(更多时候其实作为一种限制条件出现),比如 AWT/Swing 的 EDT,JS 的 Event Loop,QT 的 QEventLoop, javaFX 的 Application Thread
  • 线程事件循环机制的本质是使用多线程和某种事件通信机制,让耗时操作不在UI线程中执行,UI线程通常只处理绘制相关的操作,这样就可以一直维持界面的高响应灵敏度
  • Android的事件循环机制的构成元素主要有,一个生产者消费者模型(looper,message,queue,handler),ThreadLocal,pipe/epoll,第一个是提得比较多的,多到做android的人会觉得恶心的程度,ThreadLocal稍微少点,因为不是很重要,至于epoll,这个就更少,不过其实挺重要的,面试也可能问。handler机制里面还有一个SyncBarrier模型,最近见到有人用这个实现预加载,也可以了解一下,不过如果依赖了rx,就没必要用这个了
  • 下面会具体讲解实现,由于handler机制本身也是android面试中常问的一点,因此也会补充一下相关的问题


概览

  • 我们看Handler的注释,从注释中总结出其要点是,Handler用于发送和处理消息(Message)和Runnable对象,使用Handler机制的线程都有一个消息队列(MessageQueue),当Handler被创建时,它会与当前线程的MessageQueue绑定,并且基于该MessageQueue 发送/取出处理 Message 和 Runnable。这里里面没有提到,Looper是真正执行消息循环的对象,负责维护一个MessageQueue
/\*\*
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 \* 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 \* 
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 \* 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 \* 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 \*/
public class Handler {


线程

  • 从实际经验得知,我们使用handler时,当前线程必须要有Looper,主线程中的 Looper,是android框架提供好的,而子线程中的Looper就需要自己创建了,流程固定都是调用Looper.prepare,创建handler,调用Looper.loop
//子线程上下文
Looper.prepare();
//并不是所有handler都要放这里
//但至少要让需要的handler有机会创建
//而不是让Looper空转
xxx = new XXXHandler(); 
Looper.loop();
  • 那么主线程的Looper是怎么来的呢,其实前几篇有讲到,它是在ActivityThread的main方法里面创建的,需要注意ActivityThread本身并不是个线程,而且它也不负责创建主线程(不过倒是main倒是会创建binder线程),这个main方法也不是普通java应用里的main class的main方法,从我们之前分析的流程可以知道ActivityThread的main方法是Zygote进程fork之后执行的,这之前使用抛出异常的方式清理了堆栈,让它看起来像我们平时见到的main方法
  • 不过main方法乍看之下还是很单纯的,Looper.prepareMainLooper创建looper,new ActivityThread() 导致其成员变量的handler(就是mH)也被创建,最后调用Looper.loop这样就把主线程的Looper创建好了,之后我们的生命周期方法都是在mH处理消息时调用的
public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    SamplingProfilerIntegration.start();

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

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

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

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

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    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");
}
  • 顺便提一下 ActivityThread的attach方法,这个方法导致的操作非常非常多,之前看源码时漏了一点,它会把AppThread传递给AMS,因为AMS需要它来跨进程调度ActivityThread,AMS获取它之后反过来调用其bindApplication方法,即回到ActivityThread的进程创建Application,这里和本文主题无关,之后会移到Activity创建一篇当中
private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
            UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            BinderInternal.addGcWatcher(new Runnable() {
                @Override public void run() {
                    if (!mSomeActivitiesChanged) {
                        return;
                    }
                    Runtime runtime = Runtime.getRuntime();
                    long dalvikMax = runtime.maxMemory();
                    long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                        mSomeActivitiesChanged = false;
                        try {
                            mgr.releaseSomeActivities(mAppThread);
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                }
            });
        } 
//...


Looper

  • 按顺序来看prepare,入口要么是prepare(),要么是prepareMainLooper(),过程都会创建Looper对象,并且设置到ThreadLoal当中,区别在于prepareMainLooper需要保存sMainLooper,这个就是我们调用getMainLooper能拿到主线程的looper的原因(另外,这里加锁的原因不是很明白,虽然sMainLooper是共享变量,但是这里是刚fork出来的Zygote子进程,即使其他地方能同时走到main方法里的prepareMainLooper,也应该不在同一进程里面,因为走到这里的前提是执行了ZygoteInit,不过Android框架里面确实有很多很迷的同步操作,这里就不管了)
 /** 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));
}

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
  • 创建Looper,构造方法会同时创建MessageQueue并设置当前线程
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
  • loop 方法,开启消息处理循环,由于底层使用epoll,所以不会引发很高的cpu占用率,退出条件是从消息队列MessageQueue中使用next获取的消息对象Message对象为null,next只会在Looper(quit方法被执行,这个方法会导致MessageQueue被标记为退出状态)被终止时返回null,如果能够获取掉消息对象,就会通过Message持有的handler对象(target)的dispatchMessage方法来分发消息
/\*\*
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 \*/
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;
    // 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();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
                msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        msg.recycleUnchecked();
    }
}


Handler

  • MessageQueue 涉及比较多的native方法,我们之后再看,先来看Handler
  • Handler用来发送和处理事件,由于Handler创建时绑定了looper,也就绑定了MessageQueue,因此handler可以往looper对应的线程的消息队列发送事件,这些事件被该线程的looper处理,因此事件处理的时机和发送时所在的线程无关,所以事件总是在执行loop方法的线程里处理
  • Handler的构造参数有三个,looper,指定looper也就指定的消息处理的线程,callBack,这个callBack的回调方法也叫handleMessage,它会在handler的handleMessage之前执行并且可以根据返回值来实现消息拦截,async,这个名字其实不是很贴切,它和后面提到的同步屏障(SyncBarrier)有关,具体作用就是,设置为true时,可以在消息队列中加入同步屏障,在同步屏障之后的消息,如果是同步的话,不会被取出处理,如果是异步的话则能正常取出,具体实现可以看后面的MessageQueue部分,系统只打算用这个机制来优化UI绘制,所以设置同步屏障的方法都是@hide的,因此只能通过反射调用,这篇文章中结合了handlerThread来实现预加载,不过老实说预加载其实和handler没什么关系,基本只要有一套缓存和通知机制就可以实现了,如果使用rx,建议通过BehaviorSubject实现
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}
  • 发送事件时不管是调用post系列的方法还是调用send系列的方法,最终都会调用sendMessageAtTime这个接口,post发送的Runnable对象会被封装为message的callBack成员
/**
 * 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(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);
}
  • handler内部发送消息的实现则是enqueueMessage方法,该方法将msg
    对象的target成员设置为当前handler(就是this),这样looper在拿到msg的时候就可以同时得到处理它的handler了,这里还设置了msg的flags成员用它来标记是否启用(async是否为true)同步屏障
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
  • 至于处理消息,之前looper部分已经提到了,looper拿到msg后调用其成员target(就是刚刚设置的handler),调用dispatchMessage方法进行消息的分发,消息分发也有两种情况,一种是处理post发送的Runnable对象,毫不犹豫地调用run方法即可,一种是send发送的message对象,这里我们刚刚提到可以在handler设置的具有拦截功能的callBack就出现了,如果重写这个方法返回了true则消息不再处理,否则就交由handler进行二次处理,也就是我们平时常用的那个handleMessage方法
/**
 * Handle system messages here.
 */
public void dispatchMessage(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();
}


Message

/**
 *
 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations in many cases.
 *
 * <p class="note">While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
 * them from a pool of recycled objects.</p>
 */
public final class Message implements Parcelable {
    /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;

    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;

    /**
     * Optional field indicating the uid that sent the message.  This is
     * only valid for messages posted by a {@link Messenger}; otherwise,
     * it will be -1.
     */
    public int sendingUid = -1;

    /** If set message is in use.
     * This flag is set when the message is enqueued and remains set while it
     * is delivered and afterwards when it is recycled.  The flag is only cleared
     * when a new message is created or obtained since that is the only time that
     * applications are allowed to modify the contents of the message.
     *
     * It is an error to attempt to enqueue or recycle a message that is already in use.
     */
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    /** If set message is asynchronous */
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    /*package*/ Message next;
  • MessageQueue故名思议,消息队列,其节点数据结构为message。message比较重要的成员有 what,arg1,arg2,obj,data, target, callback,when,next,flags,replyTo,前面的,有用过handler的都知道是什么,说下最后三个
next: 下个节点的message,所以可以说MessageQueue其实是个单向链表

replyTo:客户端的Messager,在Messager机制中用到

flags: message状态的标志位,目前只有下面两种标记

FLAG\_IN\_USE:正在使用,刚创建或获取的Message都是未使用的,一旦提交给handler,直到被重新分配(obtain)或者可回收前都算使用中

FLAG\_ASYNCHRONOUS:是否为异步消息,可以联系前面的内容理解
  • 创建Message,要么直接new,要么调用obtain,Message中缓存了一个静态的链表,称为消息池sPool,实际上是个栈,每次都从表头摘下一个节点回收复用,sPool为空,才new一个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,检查Message是否在使用,否则放入表头(栈顶)
/**
 * 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.
 */
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 = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

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


MessageQueue


IdleHandler

  • MessageQueue的成员,我们只需要关注两个. mMessages,队列,一个单纯的Message对象,mIdleHandlers,闲置handler
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
  • 闲置handler,闲置不是什么都不干的意思,而是当MessageQueue里的消息都被处理完之后用来接收MessageQueue的这个idle状态的,idleHandler有一个queueIdle方法需要重写,在mMessages被looper掏空或者第一个消息指定的处理时间还没到达时调用,这种特殊的handler可以实现一些很酷的事情,比如,ActivityThread中的GC和非前台Activity的生命周期管理,分别由ActivityThread的GcIdler和Idler实现,这里我不分析源码,只给结论,有兴趣的可以自己去看
1.GC:
AMS会在某些情况下将会调用scheduleAppGcsLocked,这个方法将会可能会引发ActivityThread进行两种场景的gc
一种是低内存,一种是进程运行在后台
低内存时会回调进程中的Activity的onLowMemory,然后gc
运行在后台时相对没有那么严重,这个时候就使用到idleHandler,MessageQueue闲置后再gc
注意这里的gc指的都是手动调用runtime.gc()
基于ActivityThread中的GCWatcher机制,ActivityThread能感知到gc
不管哪种gc,ActivityThread都能在gc的同时选择释放一些Activity

2.非前台Activity的生命周期管理:
这里指的是
对pause的Activity进行stop或destroy
对Finishing的Activity进行destroy
例如前者,我们知道启动Activity时目标Activity的resume总是先于前一个Activity的stop
ActivitThread当resume完指定的Activity后也会进入idle状态,
因此这时就可以通过idleHanler去调用AMS的activityIdle方法,
activityIdle会对整个系统进行检查,大多数stop和destroy操作都由这里引发
  • idleHandler在业务上也有作用,比如glide中用来在idle的时机清除已经被回收的弱引用,另外我们也可以使用它来在界面绘制完成(注意这个时机晚于onResume)之后做一些操作,比如这篇文章中将复杂的界面设置放在绘制完成之后,这样可以先把简单的部分先显示给用户,而不会因为一部分的界面原因导致整个界面长时间处于空白状态
  • 接下来正式看MessageQueue的方法,需要关注的有enqueueMessage,插入消息,next,获取消息,quit,退出


enqueueMessage

  • enqueueMessage,插入消息,插入有两种情况,插入队头,对应情况为队列为空或消息的目标时间最早,反之则插入队中
  • 插入后队列当然已经被更新了,但执行next的线程目前可能处于阻塞状态无法处理消息。导致阻塞的原因有两种,进入idle状态(无消息,或所有消息处理时机未到),或者队头为SyncBarrier并且后面全是同步消息。因为在阻塞前设置的唤醒机制所能做的有限,所以要么设置需要被外界主动唤醒,要么设置为在达到目前队列里第一个可处理的消息的处理时机自动唤醒
  • 因此enqueueMessage还要负责唤醒next的线程,总的来说有两种情况需要唤醒,一是插入队头,因为需要直接处理这个新来的消息或者更新唤醒实际并重新阻塞,二是插入队头为SyncBarrier的队中,且这个消息比SyncBarrier后的所有异步消息处理时机都要早(队头不是SyncBarrier不需要唤醒是因为还轮不到这个消息,只要不是队头,随便插都行),注意这些判断条件不是在enqueueMessage里全部做完的
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;
            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;
}


next

  • next,由looper调用的,这个方法的作用不是单纯的把消息从队列头里拿出来,整体效果应该是,待到第一个可处理的消息的处理时机返回这个消息,这两者差别是非常大的,可以自行体会一下
  • 这个效果可以分为两个组成部分,一个是查找第一个可处理的消息,另一个是阻塞到这个消息的处理时机,我们分开来看,因为源码比较直观我就不分析了,只记录要点
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();
        }

        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;
    }
}
  • 可处理的消息指消息队列没有被退出时,SyncBarrier前的消息或SyncBarrier后的异步消息,SyncBarrier在实现上只是一个target为null的消息,消息本来就是按处理时机从前往后排的,如果MessageQueue被退出了,返回null,否则按条件取排在最前面的即可,
  • 处理时机,则是使用nativePollOnce的native方法这个方法由两个参数,一个是MessageQueue的指针,一个是阻塞的时间,阻塞时间为-1时只能由外界唤醒,next方法如果能拿到可以的消息就基于SystemClock.uptimeMillis()计算还差多少时间来设置这个参数,如果拿不到那就设为-1, 等enqueueMessage去唤醒它
  • 注意IdleHandler的调用也是在next方法里实现的,因为拿得到消息的话就可以直接return的,所以循环体的最后即处于idle状态,idle状态处理完后进入下一次迭代
    // 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);
        }
    }
}
  • quit, 这个方法设置一个mQuitting标志位,然后移除消息(参数safe为true时移除未处理的,否则移除全部),然后唤醒next,这个时候next继续执行的话将会返回null,looper接受到这个null就知道要退出循环了
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);
    }
}


native


Linux pipe/epoll

  • 主要涉及 pipe poll epoll


Looper & MessageQueue

  • 我们主要关注这两个文件
/system/core/libutils/Looper.cpp
/frameworks/base/core/jni/android\_os\_MessageQueue.cpp



未完待续