Android 消息机制Handler详解之Framework(多图)

一 概述

Handler是Android消息机制的上层接口,我们平时开发只需和Handler接触。Handler我们平时用到的也非常多,也是面试经常问到的问题。
Handler 的主要作用将一个任务切换到指定线程中去执行。

场景演示
有时候我们需要在子线程去进行一些耗时的操作,当操作完成我们需要去更新UI,Android规定操作UI必须在主线程中去执行。这个时候我们就要去切换线程,我们可以通过Handler将更新UI操作放到主线程中去执行。

Handler的运行需要底层的MessageQueue和Looper作支撑。
- MessageQueue 中文名字叫消息队列,它是用于存储消息,以队列的形式对外提供消息的插入和删除。虽然叫队列但是它的内部存储结构是以单链表的形式进行存储的。
- Looper 作用:用来处理消息。Looper以无限循环的方式去查询有没有消息,有消息处理消息,没有消息阻塞等待。
- ThreadLocal 在每个线程中互不干扰的存储并提供数据。

下面我们来看一下Handler的工作流程图
Handler工作流程

下面我就根据Handler工作流程图从源码去解读Handler工作流程。

二 源码详解

我们先从Handler源码看起吧,下一步看MessageQueue源码,最后看Looper源码。

2.1 Handler 工作原理

Handler的主要工作是发送消息 和接受消息。
消息发送过程
消息的发送我们可以通过一系列send和post方法。
来看一下这些send和post方法源码吧,简单过一下不要详细看,下面有流程图。

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

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

    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

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

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

Handler 的send和post方法是不是很多,我们来通过流程图梳理一下。
这里写图片描述
看流程图思路瞬间清晰了,Handler 的send和post方法是很多,但是最终都调用了enqueueMessage方法。我们来看一下enqueueMessage方法源码。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

msg.target 就是当前的Handler,queue.enqueueMessage(msg, uptimeMillis);就是将消息插入消息队列。发送消息的过程结束了,我们来看一下接收消息过程。

消息接收过程
当有消息时Looper调用MessageQueue的next()方法取出消息,然后调用msg.target.dispatchMessage(msg)方法,msg.target就是Handler,上面已说过,这个方法就是Handler消息的处理,我们看一下Handler的dispatchMessage源码。

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

来个Handler处理消息的流程图吧

这里写图片描述

  1. 首先检查Message的callback是否为空, Message的callback是个Runnable对象,是Handler调用post方法传递的参数post(Runnable r)
  2. Message的callback不为空时 通过调用handleCallback(msg)方法处理消息,方法源码如下,调用了message.callback(即Runnable)的run方法。

    private static void handleCallback(Message message) {
        message.callback.run();
    }  
  3. Message的callback为空时,检查mCallback 是否为空,Callback是一个接口,源码如下。

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    Handler对象可以通过CallBack方式创建:

    Handler handler = new Handler(callback);

    通过CallBack创建Handler实例但不需要派生Handler子类。

  4. mCallback 不为空时,调用mCallback的handleMessage(msg)方法处理消息。

  5. mCallback 为空时,调用handleMessage(msg)方法。

Handler处理消息的流程就到这了,下面我们来看看Handler 构造方法。

Handler构造方法

先看默认构造方法吧

 public Handler() {
     this(null, false);
}

会调用下面的构造方法

public Handler(Callback callback, boolean async) {
    ....省略
    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;
}

如果当前线程没有Looper就会抛出Can't create handler inside thread that has not called Looper.prepare()异常,这就是为什么在没有Looper的子线程中创建Handler会报这个异常原因了。

Handler还有一种特殊的构造函数,通过Looper 来构造一个Handler,构造函数源码如下。

public Handler(Looper looper) {
    this(looper, null, false);
}

2.2 消息队列(MessageQueue)工作原理

MessageQueue主要功能:消息插入和消息读取。虽然MessageQueue叫消息队列,但是它的内部实现不是用的队列,而是通过单链表数据结构来维护消息列表,链表执行插入和删除操作十分方便。我们先来看消息的插入。

消息插入

向消息队列中插入一条消息对应的方法是enqueueMessage,看一下源码。

boolean enqueueMessage(Message msg, long when) {
    ... 省略
    synchronized (this) {
        ... 省略
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //消息队列为空或者延时执行时间0或者小于队头的时间将这条消息插入队列首部
        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;
}

从源码中我们可以看出消息插入消息队列的顺序是根据时间来插入的
when是 系统开机总时间+dalayMillis(我们设置的延时时间)相对于系统启动的绝对值。看一下源码就清楚了。

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

SystemClock.uptimeMillis()就是开机到现在毫秒数

我们在enqueueMessage方法中有一个when = 0 判断,我们上面说了when是 系统开机总时间+我们设置的延时时间,不可能为0。什么情况会为0呢?
答案是:把消息强制插入队列的队首位置,会传入0

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    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, 0);// 注意 会传入0
}

消息的插入就到这里了,还有有很多的细节值得注意的。下面我们看一下消息的读取。

消息的读取
消息的读取对应的方法是next(),主要作用是从消息队列中取出一条消息并从消息队列中移除这条消息。看下源码。

Message next() {
    ...省略
    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;
            }

         ...省略
        }
        ...省略
        nextPollTimeoutMillis = 0;
    }
}

next方法是一个无限循环的方法,当消息队列中没有消息,会一直阻塞在这里。当消息队列中有消息并且到了消息的执行时间返回这条消息。

2.3 Looper工作原理

Looper会不停的从MessageQueue中查看是否有新消息,如果没有消息就会一直阻塞,如果有消息就会立即处理。Looper在Android消息机制中起到消息循环的作用。

创建Looper

我们先来看一下如何创建一个Looper,分2种情况,如下。
1. 在子线程中我们通过Looper.prepare()即可为当前线程创建一个Looper。
2. 主线程通过Looper.prepareMainLooper()为主线程(即ActivityThread)创建Looper。Looper给我们提供一个getMainLooper()方法,他可以在任何地方获取到主线程的Looper。

我们看一下Looper.prepare()Looper.prepareMainLooper()方法源码

public static void prepare() {
    prepare(true);
}

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()Looper.prepareMainLooper()方法最终都调用了prepare(boolean quitAllowed)方法,源码如下。

  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的构造函数。

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

构造函数中做了2件事。

  1. 创建了MessageQueue对象.
  2. 将当前的线程对象保存起来.

Looper开启消息循环 通过Looper.loop();方法
我们看一下源码

public static void loop() {
    final Looper me = myLooper();//获取ThreadLoad存储的Looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//获取Looper对象中消息队列

    //确保在权限检查时基于本地进程,而不是基于最初调用进程
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {//进入loop的主循环方法
        Message msg = queue.next(); // 有可能会阻塞
        if (msg == null) {//消息为空退出循环
            return;
        }

        //默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
        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.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);//用于分发Message
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

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

        //确保分发过程中identity不会损坏
        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();//将Message放入消息池
    }
}
  1. looper方法是一个死循环,跳出死循环的方式是当MessageQueue .next()方法返回msg == null时,跳出循环。那什么时候MessageQueue .next()方法返回为null呢?当Looper的quit()方法或者 quitSafely()方法被调用时,内部会调用MessageQueue的quit()方法或者 quitSafely()方法,通知消息队列退出,消息队列会被标记为退出状态,MessageQueue .next()方法会返回null.
  2. 如果MessageQueue.next()方法返回了新消息,Looper会调用msg.target.dispatchMessage(msg);方法,这里的msg.target 就是发送消息的Handler对象。Handler的dispatchMessage方法是在创建Handler时所使用的的Looper中执行的,成功的把消息处理切换到指定的线程中去了。

Looper退出消息循环
Looper退出消息循环提供了2种方法。

  1. quit()方法会直接退出Looper.
  2. quitSafely()方法会把消息队列中已有的消息处理完毕后才退出Looper.

    注意事项:在子线程中,如果创建Looper,在使用完毕应该要停止消息循环,不然会这个子线程一直处于等待状态,造成资源浪费。

最后再来一张图
这里写图片描述

猜你喜欢

转载自blog.csdn.net/zhangqilugrubby/article/details/70229003