源码 Handler

·  阅读 1063

迁移自简书 2017.09.22 17:52:33

工作流程简述

Handler 涉及的知识点有,Thread,Loop,MessageQueue ,Message。这些对象相互关联,相互配合完成 Handler 的工作。 大体流程示意图如下:

Handle

简单介绍下: Handler 通过 post Runnable 或 sendMessage 方法来发送要处理的 Message 到消息队列(MessageQueue),这个消息队列是由 Looper 创建,管理的。Thread 中可以调用 Looper 的 loop() 方法,loop() 会无限循环,从消息队列中取出待处理的消息,交给 Handler(Message 的 Target) 来处理,处理完之后,Looper 继续从队列中取下一个消息再给 Handler (Message 的 Target),直到队列中没有消息,退出循环。这个 Looper 是在线程中创建出来并运行的,被 Handler 持有。

下面会结合一些概念,源代码和示例代码介绍流程中的一些细节,帮助理解。

Handler 概念

Handler, (以下翻译自官方文档)Handler 可以发送和处理与它相关的 MessageQueue 中的 Message 和 Runnable。每个 Handler 实例都与一个单独的线程和这个线程的消息队列关联。在创建一个新 Handler 时,它与这个线程和消息队列绑定到了一起,它将消息和任务发送至消息队列,并在消息离开消息队列时执行它们。

Handler 有两个主要的用处:

  1. 在未来某个时间点去调度 Message 和 Runnable。
  2. 在不同的线程中将任务添加到消息队列(线程间通信)。

可以使用 post(Runnable) , postAtTime(Runnable, long) , postDelayed(Runnable, long) , sendEmptyMessage(int) , sendMessage(Message) , sendMessageAtTime(Message, long) , and sendMessageDelayed(Message, long) 这些方法调度消息。 post 版本的方法可以将 Runnable 对象加到消息队列中, sendMessage 版本的方法可以将绑定数据的 Message 对象用 Handler 的 handleMessage(Message) 方法处理(必需实现 Handler 的子类)。

当应用程序的进程被创建的时候,主线程会运行起来一个消息队列用来处理程序的高级对象(activities, broadcast receivers 等等 )和它们创建的任意 windows。你也可以创建自己的线程和主线程通过 Handler 进行通信。

Looper,MessageQueue 的创建和 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;
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

这是 Handler 的两个构造方法,从方法中可以看到,Handler 必须引用 Looper(mLooper) 和 MessageQueue(mQueue)对象,Looper 是怎么创建?如何传给 Handler 被引用的?可以先从下面的几段代码入手了解。

public void mainThreadHandler(View view) {
    final Handler handler = new Handler();

    final Runnable task = new Runnable() {
        @Override
        public void run() {
            String msg = "Handler Post Runnable 线程名: " + Thread.currentThread().getName()
                    + (isMainThread()?", 是":", 不是") + "主线程";
            makeToast(msg);
        }
    };

    Runnable task2 = new Runnable() {
        @Override
        public void run() {
            String msg = "thread 线程名: " + Thread.currentThread().getName()
                    + (isMainThread()?", 是":", 不是") + "主线程";
            makeToast(msg);
            handler.post(task);
        }
    };
    Thread thread = new Thread(task2, "Sub Thread01");
    thread.start();
}
复制代码

主线程创建的 Handler ,不用传入 Looper,因为主线程已经调用了 prepareMainLooper(); 方法,拥有了 Looper。而 Handler 是通过前面第一种构造方法中 mLooper = Looper.myLooper(); 代码获得的 Looper。

现在来看 Looper.myLooper(); 中的代码,了解获取 Thread 中 Looper 的过程。

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

//public class ThreadLocal<T>
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码

Looper.myLooper(); 调用了 sThreadLocal.get() 方法,获取当前线程的 ThreadLocalMap 对象,这个对象就是用来存储 Looper 的,通过 ThreadLocal 做为 key 获取到对应的实体,从而获取我们想要的值,Looper。

哪 Looper 又是怎样存储的呢?

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

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 的 prepare 方法,只不过主线程调用的是 prepareMainLooper() ,子线程调用 prepare(boolean quitAllowed) 。 必要的校验后,new 出一个新 Looper 对象,使用 sThreadLocal.set(value) 方法存储该 Looper 到当前线程中。这就和之前获取 Looper 的方法对应了起来。

MessageQueue 是在创建 Looper 过程中创建出来的,并被 Looper 所引用。

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

至此,Handler 相关的几个重要的部分都被创建了出来。

将消息添加到消息队列

下面的代码,使用的是传入 Looper 的 Handler 构造方法。

public void testHandler(View v) {
    final Runnable postTask = new Runnable() {
        @Override
        public void run() {
            String msg = "post: " + (isMainThread() ? "是" : "不是") + "主线程, 线程名:" + Thread.currentThread().getName();
            SecondActivity.this.makeToast(msg);
        }
    };

    final HandlerThread outerThread = new HandlerThread("HandlerThread");
    outerThread.start();

    final Runnable threadTask = new Runnable() {
        @Override
        public void run() {
            /* 1 */
            Looper.prepare();
            final Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    if (msg.what == 1) {
                        String msgStr = "send: " + (isMainThread() ? "是" : "不是") + "主线程, 线程名:" + Thread.currentThread().getName();
                        SecondActivity.this.makeToast(msgStr);
                    }
                }
            };
            handler.post(postTask);
            Message message = new Message();
            message.what = 1;
            handler.sendMessage(message);
            Looper.loop();

            /* 2
            final Handler handler = new Handler(outerThread.getLooper());
            handler.post(postTask);
            */

            /* 3
            final Handler handler = new Handler(Looper.getMainLooper());
            handler.post(postTask);
            */
        }
    };
    Thread t = new Thread(threadTask, "subThread");
    t.start();
}
复制代码

注释1,传入的 Looper 是在 subThread 这个线程中创建的,postTask 和 sendMessage 都会在线程 “subThread” 中执行。 注释2,传入的 Looper 是在 HandlerThread 这个线程中创建的,postTask 会在线程 “HandlerThread” 中执行。 注释3,传入的 Looper 是在主线程中创建的,所以 postTask 会在主线程中执行。 以上线程中创建 Looper 的方法都是一样的,都遵从前面介绍的过程,handler 不同的构造方法大同小异,目的都是获得 Looper。

Post Runnable 其实和 SendMessage 是一样的,最终还是封装成 Message 发送出去,供 Handler 处理。

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

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

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

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

//Message Queue
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;
}

复制代码

代码虽长,但能展示清楚发送消息的过程。先看 post(Runnable r) ,里面调用的是 sendMessageDelayed ,原来 post 方法实际上也是发送消息,但是 Message 对象在哪呢,再看 getPostMessage(Runnable r) 方法中,创建了一个 Message 对象,并把 Runnable 传给了它的 callback。至此,我们明白了 post 和 send 两种方法的区别。 sendMessageDelayed 方法调用 sendMessageAtTime ,而 sendMessageAtTime 中则是获取到消息队列 mQueue 调用 enqueueMessage ,将 Message 消息添加到了队列里。 MessageQueue 中是使用链表的数据结构来管理队列的, mMessages 是整个链表的第一个 Message 元素,下一个 Message 则是 mMessages.next,enqueueMessage(Message msg, long when) 方法的作用就是按照 when 时间来排序,插入新的 Message到整个 Message 链表中。 这个就是整个将消息添加到消息队列的过程。

消息是怎么 one by one 取出来处理的

...
final Runnable threadTask = new Runnable() {
	@Override
	public void run() {
	Looper.prepare();
	final Handler handler = new Handler();                			handler.post(postTask);
	Looper.loop();
	}
};
Thread t = new Thread(threadTask, "subThread");
t.start();
复制代码

线程中如果只调用 Looper.prepare(); 是无法使 Handler 处理消息的,为什么呢? 因为没有让这个 Looper 运转起来,去取消息给 Handler。只有使用了 Looper.loop(); 方法才能让 Handler 正常的处理消息。 哪就来看看 Looper.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;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}
复制代码

loop() 方法中启动了一个无限循环,通过 queue.next 方法获取一个消息,如果获取的是 null 则意味着消息队列没有了消息,退出循环。 如果有消息,则调用 msg.target.dispatchMessage(msg); 方法,msg.target 就是 Handler ,在前面的 sendMessage 源代码中可以看到 msg.target = this;

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();
}
复制代码

该方法中,如果有 callback 就调用 callback 的 run 方法,这对应的就是 handler.post(runnable)。而如果没有 callback 则调用 Handler 的 handleMessage 方法,这对应的就是 handler.sendMessage(message) 方法,需要 Handler 重写 handleMessage 方法来处理消息。

完成消息的处理后,则将该消息的数据重置,标记成已使用,放入重用池中等待重用。至此一个消息取出处理就完成,再发起新一轮循环。

消息的重用

创建消息时,如果使用 Message message = Message.obtain(); 方法创建 Message 会比直接 new 出来的更省内存,原因就是 Message 是可以复用的。

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();
}
复制代码

如果有 sPool 重用池(实际上就是 Message 单向链表的头元素),则取出,重置状态后 return 出去。sPoolSize 减一。没有重用池则 new Message。

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++;
        }
    }
}
复制代码

在介绍 loop() 的时候,处理完消息,会调用该 msg 的 recycleUnchecked 方法,这个方法的作用看源代码可知,重置消息的数据,标记为已使用,并且当重用池中消息的个数小于规定的 MAX_POOL_SIZE 时,则将重置后的消息插入到重用池链表中,置于第一个位置。 至此,消息的复用也介绍完了。

总结

通过这个简单的示意图,能够清楚的知道整个 Handler 的运作流程。通过不同部分的源代码,了解到不同流程部分的细节,线程如何创建 Looper,并启动 looper。Handler 如何发送消息到消息队列,looper 如何从消息队列中去除消息交给 Handler 处理,消息如何被复用等。

分类:
Android
标签: