Android Handler的原理

1. 简介

众所周知主线程(也叫UI线程,进行UI的更新)是不能够进行任何耗时操做,好比耗时的计算,访问服务器等等。那若是开启子线程进行复杂计算之后想要把结果传递给主线程进行结果的展现(UI更新),这个时候该怎么办比较好呢。java

有两种方法,第一种是用AsyncTask,另外一种就是要讲的Handler了。固然AsyncTask和Handler用途和原理有所不一样,因此应该根据场景选择使用。服务器

Handler是什么?Handler是在Android中用于消息的传递。Android中的主线程会维护一个Looper和MessageQueue,来保证内部的信息传递,固然咱们也能够使用它们来完成咱们的逻辑。简单说明一下Looper和MessageQueue。async

Looper: 它的主要做用是管理维护MessageQueue,从队列中取出消息传递给Handler来处理。 MessageQueue: 它的主要做用是存储消息,当队列为空时进行队列阻塞。ide

2. 消息传递处理流程

  1. Handler在线程A中发出消息,传递到MessageQueue中进行储存。
  2. Looper在线程B中取出MessageQueue中的消息。
  3. Looper在线程B中把消息传递给Handler。
  4. Handler在线程B中处理消息。

一个Looper对应一个线程。若是在主线程中发送的Message存储到共同维护的MessageQueue中,在另外一个子线程的Looper进行loop(用于去消息的方法)取消息而后传递给Handler,则Handler会在Looper所在的线程进行消息的处理。oop

整个流程以及关系以下。 源码分析

3. 源码分析

  1. 首先从Handler的源码开始分析,以下。
public Handler() {
        this(null, false);
    }

    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()");
        }
        //获取Looper对象的mQueue属性,mQueue 就是MessageQueue对象。
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

Handler中会经过Looper.myLooper()来获取获取当前线程的Looper,若是为空则会抛出异常。因此在子线程中建立Handler,则首先要经过Looper.prepare()来建立Looper,再经过Handler传递消息。值得注意的是主线程中已经默认建立了Looper,因此在主线程不要再单首创建Looper,若是建立则会抛出异常。post

  1. 再看一下Looper.myLooperLooper.prepare()的源码。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //建立当前线程的Looper对象
    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对象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码

经过prepare()方法就能够知道每一个线程中只能有一个Looper,这也是为何不能在主线程建立Looper的缘由。myLooper()来获取当前线程的Looper对象。 还有值得注意的细节是,管理Looper的数据类型是ThreadLocal,它一个线程内部的数据存储类,经过它存储的数据只有在它本身的线程才能获取到,其余线程是获取不到的。与之相对应的是ThreadGlobal是能够获取全局任意一个线程。因此在这里sThreadLocal.get()是获取当前线程的数据的意思。ui

  1. 接下来继续看一下Looper。
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); //实例化MessageQueue对象
        mThread = Thread.currentThread(); //当前线程
    }
复制代码

Looper对象中建立了MessageQueue(消息队列,用于存储消息)对象以及获取了当前的线程。由于Looper的建立是私有的,因此外界想要获取Looper对象只能经过Looper.prepare()方法。this

  1. 发出消息(sendMessage)

无论是handler.sendMessage(msg)handler.post(runnable)最底层的方法都是下面的 enqueueMessage()方法。经过这个方法把消息存到了MessageQueue中。spa

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this; //this是Handler对象。
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把消息存到MessageQueue,并返回存储成功失败的结果
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码
  1. 取出消息

消息的取出用的是Loooper.loop()方法。首先看一下代码。

public static void loop() {

        final MessageQueue queue = me.mQueue;
        //死循环
        for (;;) {
            //从MessageQueue中取出一条消息
            Message msg = queue.next(); 
            if (msg == null) {
                // 若是没有消息了,直接跳出循环
                // 可是正常状况下这段并不会被调用
                return;
            }
            //把消息交给Handler处理。
            msg.target.dispatchMessage(msg);
        }
    }
复制代码

会发现上面代码中有一个死循环,经过这个死循环不断的从MessageQueue中取出消息。当queue.next()没有获取到消息则会被阻塞,知道有消息被存放到MessageQueue中。当获取到了消息,则经过dispatchMessage()来分发给Handler处理消息。

  1. 处理消息

消息最后到达了Handler的dispatchMessage()方法。让咱们看一下代码。

public void dispatchMessage(Message msg) {
        // 若是Message有本身的callback,就由Message的callback处理
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
             //若是Handler有本身的mCallback,就由Handler的mCallback处理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //默认的处理消息的方法
            handleMessage(msg);
        }
    }
复制代码

经过代码就能够知道消息处理的优先级。

  1. 优先级最高的是message中本身的回调,这里的本身的回调是handler.post(runnable)中的runnable(msg.callback = runnable)。
  2. 若是message中没有设置回调,则判断Handler中是否有本身的回调。这里的回调是咱们在Handler中重写的handlerMessage()方法。
  3. 最后是默认的内部的handleMessage()方法。

源码的分析差很少了,下一个文章是Handler的具体的使用方法和示例。

Handler使用方法文章:juejin.im/post/5e2c04…

相关文章
相关标签/搜索