Android:异步消息Handler源码解析

前言

关于异步消息的基础用法,在子线程发送消息到主线程,可以看之前的一篇文章http://blog.csdn.net/leelit/article/details/45196827,而现在这篇文章主要是从源码角度来解析这套异步机制。


Handler经典用法

class MyThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

Handler官方文档的使用教程就是上面这个例子,我也一直认为学习Handler不能只看子线程发送消息给主线程这个最常用的场景。
通过上述步骤,我们便可以在其他线程,通过Handler来发送消息给当前线程MyThread,使得想要进行的任务在该线程执行,达到异步效果。run方法里一共就三步,接下来我们一步步来分析。注:本文后续所有的当前线程,都是指MyThread。


流程

1、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");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

prepare方法给ThreadLocal对象set了一个Looper对象,由Looper类的静态成员变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();可知,
每条线程关联一个Looper,当然如果没有在run方法里调用prepare方法,该线程就无法通过Looper类的sThreadLocal get到实例。

而Looper最核心的点就是组合一个消息队列对象

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

小结第一步:调用prepare方法,当前线程关联一个Looper对象,Looper对象组合消息队列对象。

2、mHandler = new 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;
    }

第二步实例化Handler对象,Handler对象组合一个Looper对象,取得这个Looper对象的消息队列,后续Handler可以发送消息到该消息队列。

3、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;

        ...

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ...

            msg.target.dispatchMessage(msg);

            ...
        }
    }

当前线程死循环轮询,Looper对象不断从消息队列里面拉取Message,并且是阻塞式的。假设拉到了Message,则调用:

msg.target.dispatchMessage(msg);

将消息回调给msg.target,而msg.target正是Handler对象,dispatchMessage分发方式对应三种使用Handler的方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); // Handler.post
    } else {
        if (mCallback != null) { // 实例Handler时传入的Callback参数
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); // 实例Handler时重写的handleMessage方法
    }
}

小结第三步:当前线程死循环轮询消息队列,一旦取得消息,在当前线程执行Handler回调方法。


总结

盗图一张,from《第一行代码》
这里写图片描述

(1)Looper对象在某条线程里死循环轮询消息队列;
(2)Handler组合Looper对象,取得其消息队列;
(3)Handler发送消息到消息队列,Looper在队列里取得消息后在该线程执行Handler回调。

最后还有一个问题,Handler实例时可以直接传入Looper参数,这样回调便发生在该Looper关联的线程,比如最常见的发送消息到主线程:

new Handler(Looper.getMainLooper()).post(...);

如果不传入Looper参数,则默认使用当前线程的Looper,所以在某条线程实例化Handler前必须先调用Looper.prepare,使得该线程关联有Looper对象,并且还需要调用Looper.loop(主线程也是有这两个动作的,在ActivityThread这个程序入口类的main方法里),否则即使通过Handler发送了消息Looper也不会取出来进行回调处理。


用HandlerThread实现消息队列

假如我们想要实现自己的一套消息队列来处理特定业务,咋整呢,Google已经帮我们做了所有工作了,直接使用HandlerThread即可。

HandlerThread部分源码

public class HandlerThread extends Thread {
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        ...
        Looper.prepare(); // 这条线程关联Looper
        mLooper = Looper.myLooper(); // 这条线程的Looper
        Looper.loop();  // 让这条线程的Looper开始轮询消息队列
        ...
    }
}

我们只需要做下面的动作:

public static Handler myBusinessHandler;

public static void method(){
    HandlerThread thread = new HandlerThread();
    thread.start();
    myBusinessHandler = new Handler(thread.getLooper());
}

调用完上面这个方法后,就可以用myBusinessHandler来发送消息了,然后Looper会在这条线程里回调你的任务,就是这么简单^_^

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值