Handler线程切换实现原理

来源:互联网 发布:知乎怎么那么多出国的 编辑:程序博客网 时间:2024/06/06 03:05

第一步 : Handler机制中相关对象的关系

为了更好的理解Handler机制, 我们首先来看一下Handler机制中相关对象的关系图.

首先来张Handler机制中相关对象的关系图

  • 如果了解过ThreadLocal这个类的应该会知道, ThreadLocal对象会为每一个线程创建一个变量副本. 下面来看Looper#prepare()方法源码.
// ThreadLocal 变量static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();/*** 为当前线程开启一个Looper的方法.*/private static void prepare(boolean quitAllowed) {    // 每个线程智能开启一个Looper    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    // 将新新创建的Looper对象保存到ThreadLocal变量副本中.    sThreadLocal.set(new Looper(quitAllowed));}

结合上面的代码和关系图可以得出结论 : 每个Thread对象可以持有一个Looper对象.(需要开启)

  • 接下来看下Looper的构造方法,和部分代码片段
final MessageQueue mQueue;final Thread mThread;// Looper 构造方法private Looper(boolean quitAllowed) {    // 创建一个消息队列.    mQueue = new MessageQueue(quitAllowed);    // 持有线程引用.    mThread = Thread.currentThread();}

通过上面的代码我们可以得出结论 : Looper对象持有一个消息队列.也就是关系图中的第二层关系.

  • 接下来研究下MessageQueue和Message的关系. 同样是找源码

MessageQueue中有Message链表头.

// Message链表头.Message mMessages;

Message部分源码

// 下一个元素引用.Message next;

熟悉链表结构的朋友通过上面的代码应该就清楚网络. 为什么MessageQueue中的mMessages是一个链表的头. 因为Message对象中可以保存下一个Message对象的引用, 因此可以形成链表结构.不明白的朋友可以看下链表结构的相关知识.

这也证实了MessageQueue是消息队列的. 他其实就是一个链表结构.

  • Handler 与 MessageQueue和Looper对象的关系

Handler构造方法部分源码

// Looper 引用final Looper mLooper;// MessageQueue引用final MessageQueue mQueue;public Handler(Callback callback, boolean async) {    ...    // 获取当前线程的Looper对象, 并保持.    mLooper = Looper.myLooper();    // 如果当前线程没有启动Looper则抛出异常    if (mLooper == null) {        throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()");    }    // 引用Looper 的MessageQueue对象    mQueue = mLooper.mQueue;    mCallback = callback;    mAsynchronous = async;}

通过上面的代码可以正式关系图中的引用关系是正确的也就是.

  1. Handler中持有和创建Handler所在线程绑定的Looper对象.
  2. Handler中持有和创建Handler所以在线程绑定的Looper的MessageQueue的一个引用. 有了这个引用在Handler发送消息的时候其实就是将详细保存到这个MessageQueue中.

    • Handler和Message的关系

Message源码片段

// Handler引用Handler target;

Handler源码片段

// Handler 一系列发送消息的方法最终都会调用这个方法.private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    // Message对象持有当前的Handler对象.    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    // 调用了MessageQueue的enqueueMessage    return queue.enqueueMessage(msg, uptimeMillis);}

通过上面的代码我们可以确定了Message确实持有发送他的Handler对象的引用. 有了这种关系, 在处理消息时才会到Handler#handleMessage方法中处理.

结论 :
通过上面的分析我了解了相关对象的引用关系, 清楚了这些关系下面的Handler机制就很好理解了.


第二步 : 结合源码分析Handler机制实现原理

其实Handler机制的实现原理很简单, 就是通过共享变量来实现的. 在Handler机制中充当共享变量角色的就是MessageQueue对象.发送消息的一方会将Message保存到MessageQueue中, 接收消息的一方会一直轮询MessageQueue是否有可以处理的消息.让后通过 synchronize关键字来处理线程并发问题.

1. Handler创建前提和消息处理

主要就是消息处理方创建充当共享变量角色的MessageQueue对象. 同时以轮询的方式查询消息队列中是否有可以处理的消息. 获取有用消息然后处理

为一个线程创建和开启Looper的方式如下

new Thread(new Runnable() {    @Override    public void run() {        // 创建        Looper.prepare();        // other code        // 开启        Looper.loop();    }});
  • Looper.prepare();源码分析
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");    }    // 创建Looper    sThreadLocal.set(new Looper(quitAllowed));}

其实就是简单的创建一个新的Looper对象并和当前线程绑定.

  • Looper.loop(); 源码分析

源码片段如下.

public static void loop() {    // 获取当前Looper    final Looper me = myLooper();    // 如果没有Looper则抛出异常    if (me == null) {        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");    }    // 得到消息Looper中的消息队列对象.    final MessageQueue queue = me.mQueue;    // 无限循环.    for (;;) {        // 获取消息队列中下一消息, 如果消息队列中没有可用的消息则阻塞.        // 注意 : 此处可能会阻塞        Message msg = queue.next(); // might block        // 如果获取到null则说明消息队列已经退出.则退出循环        if (msg == null) {            // No message indicates that the message queue is quitting.            return;        }        // ...        try {            // 处理消息            // 通过消息获取到发送消息的Handler对象并调用Handler的dispatchMessage()方法            msg.target.dispatchMessage(msg);        } finally {            if (traceTag != 0) {                Trace.traceEnd(traceTag);            }        }        // ...        msg.recycleUnchecked();    }}
  1. 通过上面的代码我们可以看出来在Loop()方法中主要就是轮询消息队列. 退出轮询的条件就是消息队列中返回null.
  2. queue.next(); : 方法会可能会阻塞.
  3. 获取到消息后通过 Message对象target字段获取到发送消息的Handler对象. 同时调用Handler对象的dispatchMessage(msg) 方法.

    • MessageQueue#next() 源码分析
// 查询消息Message next() {    // 死循环    for (;;) {        // 2. 操作MessageQueue需要同步, 下面的操作就是尝试获取一个可用的Message        synchronized (this) {            // 3. 获取当前时间            final long now = SystemClock.uptimeMillis();            // 4. 获取MessageQueue头元素            Message prevMsg = null;            Message msg = mMessages;            // 5. 判断消息是否正常, 有可能会出现消息还未处理, 但是Handler已经被销毁了.所有msg.target= null            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) {                // 6. 判断是否到了执行时间                if (now < msg.when) {                    // 7. 下一个下次不满足执行条件                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                } else {                    // 8. 获取到可执行的消息                    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;            }            // 判断是否退出了.            if (mQuitting) {                dispose();                return null;            }        }    }}

代码中已经有了详细的注释, 从代码中我们可以看出next() 方法退出的情况有两个
1. MessageQueue退出标志被设置, 此时返回 null.通过上面loop() 方法的源码我们知道在这种情况下Looper 的处理时退出Looper.
2. 找到要处理的消息, 此时返回Message对象.在正中情况下是调用msg.target.dispatchMessage();

  • Handler#dispatchMessage()源码
public void dispatchMessage(Message msg) {    if (msg.callback != null) {        // 回调        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        // 我们熟悉的方法.        handleMessage(msg);    }}

看了源码是不是豁然开朗. handleMessage(msg);不就是我呢经常重写的方法吗.
消息的处理至此结束.

2. 创建Handler

主要就是发送方持有消息处理方创建的共享变量的操作.

创建Handler的方法如下

new Thread(new Runnable() {    @Override    public void run() {        Looper.prepare();        // 创建Handler        Handler handler = new Handler(){            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);            }        };        Looper.loop();    }});

Handler构造方法

public Handler(Callback callback, boolean async) {    // ...    // 保存Looper    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;}

创建Handler的过程中主要就是做了两件事
1. 保持当前线程的Looper对象的引用.
2. 保持当前线程的Looper对象的消息队列. 也就是Looper和Handler持有同一个消息队列的引用. 前面说过MessageQueue充当了共享变量的角色.

3. Handler发送消息

其实就是将需要发送的消息到消息队列中.

发送消息的核心方法

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);    }    // 调用MessageQueue的enqueueMessage方法将消息放入消息队列.    return queue.enqueueMessage(msg, uptimeMillis);}

接下来我们来看MessageQueue#enqueueMessage部分源码

// Message入队方法boolean enqueueMessage(Message msg, long when) {    // 1. 如果Message的target为null则报异常.    if (msg.target == null) {        throw new IllegalArgumentException("Message must have a target.");    }    // 2. 消息正在使用.抛出异常    if (msg.isInUse()) {        throw new IllegalStateException(msg + " This message is already in use.");    }    // 3. 操作MessageQueue需要同步    synchronized (this) {        // 4. 判断是否退出.        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;        }        // 5. 设置message相关标记        msg.markInUse();        msg.when = when;        Message p = mMessages;        boolean needWake;        // 6. 入队列        if (p == null || when == 0 || when < p.when) {            // New head, wake up the event queue if blocked.            // 6.1 第一个消息, 或者是需要立即处理的消息.            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            // 6.2 Message消息队列是按照执行的开始时间来排列的.             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. 首先会检查Message 对象是否设置了target方法, 如果没有则抛出异常. 检查的原因也很简单,我们知道Looper中的收到消息后调用msg.target.dispatchMessage() 方法, 如果不设置就会造成空指针异常, 再说如果不设置也没有办法处理消息, 下次也就没有了意义.
2. 检查Message对象是否正在使用. 这个检查也是有必要的. 我们知道消息时通过链表来保存的, 如果是一个已经在链表中的对象我们在此修改他的引用指向就可能破快链表.
3. 前面说过MessageQueue充当了共享变量的角色因此, 需要使用synchronized处理同步问题.
4. 如果调用了MessageQueue方法的quit方法就可以退出消息循环.
5. 设置先关标记
6. 到了这里Message就是一个正常的消息, 可以入队列. 首先我们要明白一点 MessageQueue中的消息都是按照Message的开始执行时间来排序的.因此越先执行的就越靠前.
1. 如果消息队列中没有消息,则这个消息肯定是放在头部, 同时入股是立即执行的消息也是要放在最前面的.因为他的开始时间最早.
2. 如果不满足 1 的条件则很具开始时间来查找插入位置进行插入.

至此消息发送就已经完成了. 接下来就是消息处理部分了, 前面已经讲了.

第三步 : 结论

通俗点的说法Handler机制其实就是借助共享变量来进行线程切换的.

原创粉丝点击