Android 消息机制源码笔记

Table of Contents

本笔记是Android中对Handler相关原理的代码阅读笔记.

Message

该类是描述了用于作为信息载体的"消息"的实现, 几个重要的成员变量.

  描述
what 该变量可以用来唯一的标志条消息, 因为每个handler都有自己的命名空间, 所以发给不同handler的message, 即使what相同, 也不会冲突
arg1/arg2 如果传送的消息只是简单的int类型, 可以直接使用这俩
when 消息的激活时间
data Bundle类型, 发送的数据, 通过setData()设置
target 目标Handler
callback  

消息池

Message类本身也定义了一个消息池, 通过链表方式存储, 变量 sPoolSize 定义了消息池的大小, 消息池的操作都是线程安全的.

获取消息

obtain(…)函数族用于获取消息, 这些obtain()函数通过不同的接受不同的 参数来初始化获取到的消息. 核心函数是obtain(). 通过同步方式 从消息池中取一个消息. 也可以调用obtain(Handler h)来设置获取到的消息的 Handler.

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

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

发送消息

调用sendToTarget()函数调用Message的target变量(一个Handler)的sendMessage()函数. 如果没有设置target则会报错. sendMessage()函数的实现在Handler一节讲述.

public void sendToTarget() {
    target.sendMessage(this);
}

MessageQueue

该类基于前面的Message类实现了一个消息队列, 用于"顺序"处理 消息, 消息按照其产生时间先后排序.

插入消息: enqueueMessage(msg, when)

该函数用于插入一个消息, 由于消息队列按照消息时间when升序排序,所以 新插入消息会遍历列表, 并插入到相应位置.
下面是该函数代码:

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

函数的流程如下:

  1. 首先判断该msg是否有target或正在被使用.
  2. 获取消息队列的锁, 进入同步操作.
    1. 如果队列正在退出, 回收新消息, 并返回.
    2. 否则, 插入到相应位置
    3. 判断是否需要唤醒, 如果是则唤醒.

从队列获取消息

通过函数next()从队列中获取一个消息. 下面是该函数代码, 代码流程:

  1. 调用JNI函数nativePollOnce(ptr, timeout), 该函数的第二个参数表示要 阻塞的时长, 如果为0则立即返回, 如果为-1则一直阻塞.
  2. 试图获取一个消息.
    1. 如果没有, 则将上一步的timeout变量nextPollTimeoutMillis设为-1.
    2. 否则(即有消息),
      1. 如果消息时间大于当前时间(即消息的执行时间还未到来), 则设置 nextPollTimeoutMillis的值为差值.
      2. 否则, 返回消息.
  3. 获取消息失败, 继续走循环后面的内容.
    1. 如果队列正在退出, 调用dispose()函数销毁native的消息队列.并返回null. (在Looper中, 这一步会导致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;
             }

             ...
     }
 }

Looper

Looper类用于在线程中实现一个"消息循环"行为. Looper有一个MessageQueue类型的变量mQueue用于存储消息.

为线程初始化一个looper

Looper类有一个静态变量sThreadLocal, 该变量是一个ThreadLocal 类型的线程私有变量. 当调用prepare()函数进行初始化时, 会在函数内部生成一个looper实例并赋值给该变量. 调用 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));
}

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

PS: Looper还有一个静态变量sMainLooper, 这个变量是UI线程 的Looper引用, 在应用启动时被初始化.

loop()函数处理消息

函数的处理在loop()函数中, 该函数建立了一个"无限循环", 每次循环都从消息队列中获取一个消息, 若无消息则可能 会阻塞或者退出循环(主要与MessageQueue有关. 下面是该函数主要代码:

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

        ...

        msg.target.dispatchMessage(msg);

        ...

        msg.recycleUnchecked();
    }
}

代码流程:

  1. 调用queue.next()函数从队列获取消息.
  2. 判断消息是否为空, 如果为空则退出循环(线程也可能退出). 因为next()函数可能会导致线程阻塞. 所以如果"被唤醒"还拿到空消息, 有可能是别的线程调用了quit()函数.
  3. 调用msg的target变量(即Handler)的dispatchMessage()函数.
  4. 调用Message的recycleUnchecked()函数回收消息.

Handler

在一般的APP开发中, 都是通过handler进行消息的发送或 处理. 这里是几个主要功能的代码笔记.

创建handler

Handler的构造函数有多个, 基本最后都调到下面两个函数之一:

  1. Handler(callback, async). 第一个参数callback的用于, 如果不想自己写一个Handler的子类 (Handler的通常用法), 可以传入一个callback参数用于处理消息. 第二个参数async标志消息是否要按时间排序. 该函数会去拿去当前线程的Looper, 如果没有则报错.

       public Handler(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();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
  2. Handler(looper, callback, async). 第一个参数looper是显示的传入一个looper参数给handler的构造函数. 这样即使当前线程没有looper也可以.

获取一个消息

调用obtainMessage()可以获取一个消息, 函数内部通过 调用Message的obtain()函数实现.

发送消息

Handler的发送消息相关的函数也有多个, 基本都是先计算该message 的执行时间, 然后调用sendMessageAtTime()函数. 该函数内部调用了 enqueueMessage()函数, 最终调用到了MessageQueue的 enqueueMessage()函数.

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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

发送runnable

post系列函数用于发送一个"Runnable"消息, 该runnable会被存入 消息的callback变量. 在Looper做消息分发时, 会回调到Handler的 dispatchMessage()函数来处理callback.代码如下:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

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

//called from looper
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();
}

Created At <2016-06-11 Sat 23:25> by Luis Xu. Email: xuzhengchaojob@gmail.com