深入解析Android Handler

1、背景

在开发中我们经常会使用到Handler,对Handler的使用应该是比较熟悉的,但对Handler的工作原理可能还不是很清楚,今天来带大家深入学习Handler的工作机制。一般使用Handler的场景如下:

  1. 子线程和UI线程进行通信,例如:子线程负责耗时操作,并通过Handler把的结果传递到主线程。
  2. 可以使用Handler发送一个延时操作

在分析Handler时会涉及到一些类如:ThreadLocal、 Looper、MessageQueue、 Message,所以在分析Handler的工作机制前,需要了解这些类的作用。

2、相关知识点

  • ThreadLocal
    功能描述:用于为每个线程保存一个本地变量,解决多个线程访问临界资源时需要进行互斥访问的问题。在Handler的工作机制中,使用ThreadLocal为每个线程保存一个Looper对象。
    即每个线程有一个Looper对象。

  • Looper
    功能描述:Looper是一个循环器,内部实现是一个死循环的从消息队列中取出消息并处理。每个线程对应一个Looper对象,一个线程初始情况下是没有Looper对象的。用户需在要创建Looper的线程中调用Looper.prepare( )方法,为该线程创建Looper对象。

  • MessageQueue
    上面在阐述Looper的作用时说过,Looper的内部实现是在内部通过循环不断从消息队列中取出消息并处理,而这个消息队列就是MessageQueue,每个Looper对象内部有一个消息队列MessageQueue。

  • Message
    Message就是保存在消息队列中的一个消息,Looper循环读取的就是一个个Message对象。

Handler工作流程如下:
这里写图片描述

3、分析

从上面的工作流程图中,Looper的工作比较关键,所以先从Looper的创建和启动开始分析。
一个线程初始情况下是没有Looper对象的,用户需在线程中调用Looper.prepare( )方法为该线程创建一个Looper对象。
我们先看Looper中包含哪些重要的属性

public final class Looper {
    // 用于保存每个线程的Looper对象
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    // 消息队列,Looper内部实现就是从该队列中取出消息并处理
    final MessageQueue mQueue;

    // 该Looper所属的线程
    final Thread mThread;
    ...
}
private static void prepare(boolean quitAllowed) {
    // 如果当前线程已创建Looper对象,则抛出异常,一个线程只能有一个Looper对象
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 创建一个Looper对象,并保存到TheadLocal
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
     // 创建了一个队列
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper对象已经创建完毕,但是这时Looper对象还没有开始工作,需手动调用Looper.loop()方法

public static void loop() {
    ...
    // 开启一个死循环不断的从队列中取出消息
    final MessageQueue queue = me.mQueue;
    for (;;) {
        // 如果Message是null,那么就Looper就会停止工作
        Message msg = queue.next();
        ...
        // 取出msg后,由msg中指定的target来处理该msg,target是msg指定的处理对象
        msg.target.dispatchMessage(msg);
        // 回收Message对象
        msg.recycleUnchecked();
    }
}

MessageQueue就是实现一个队列的功能,用于存储消息,内部通过一个链表的方式来实现队列的数据结构。该队列不止提供先入先出的排队功能,还可以优先使某种类型的消息先出栈。在分析Message时会详细讲解。
Message就包含了两种信息:

  • 指定由谁来处理Message,在上面的分析中Message就是有target指定的对象来处理Message的
  • 携带数据,这样我们就可使用Message在不同线程中进行通信和传递数据

下面来看一下Message的源码
target就是Message内部指定的用于处理该Message的处理器,类型为Handler。

public final class Message implements Parcelable {
    ...
    Handler target;
    ...
}

Message中用于存储数据的属性如下,三个int类型,一个对象的引用,还有一个Bundle类型,我们可以根据需要选择使用不用的属性进行传递数据

public final class Message implements Parcelable {
    ...
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    Bundle data;
    ...
}

在Looper的内部,但取出一个Message后,直接交个Handler.dispatchMessage()来处理

// 参数msg就是待处理的消息
public void dispatchMessage(Message msg) {

    if (msg.callback != null) {
        // callback是Message指定的动作,当Message指定callback,那执行Message.callback这个动作
        handleCallback(msg);
    } else {
        // 如果Message没有指定动作,则有Handler来处理

        // 优先交给Handler指定的mCallback处理,如果mCallback返回true,说明mCallback已经处理掉,不再传递给handleMessage()
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 我最常用的就是重写handleMessage()这个方法来处理Message
        handleMessage(msg);
    }
}

Message的创建方式

  • new Message()创建新消息
  • Android提供一个Message缓存池,通过Message.obtain( )从缓冲池中取出一个Message

提示:在Android开发中使用到Message地方非常多,不能要用Message就创建一个新的,这样频繁的创建和回收对象影响性能,所以提供Message缓存池,提高Message的使用率

public final class Message implements Parcelable {
    // 通过构造函数创建
    public Message() {
    }
    ...
    // 缓存池的数据结构为一个链表,sPool为链表的头 
    private static Message sPool;
    private static int sPoolSize = 0;

    // 缓存池中最多可以缓存50个Message
    private static final int MAX_POOL_SIZE = 50;

    // 从缓存池中获取
    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();
    }
}

那么Message对象什么时候指定消息处理器,即Message.target属性什么时候赋值?

接着分析Handler,个人认为Handler在消息机制中扮演两种角色:

  1. 为开发者提供Handler.postMessageXXX( )接口,用于添加一个消息到队列MessageQueue
  2. Message对象指定的用于处理Message消息的处理器,这时Handler扮演的是消息处理器

在创建Handler对象时,需要为Handler对象指定一个Looper,如果不指定Looper对象,则默认使用当前线程的Looper对象,构造方法如下:

// 构造方法可以传入一个Looper对象
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}


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()");
    }
    // 每个Handler对象,都作用于一个特定的消息队列上
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

当通过Handler.postMessageXXX( )接口发送消息时,最终都会调用enqueueMessage( )方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 指定了Message对象的处理器为当前的Handler对象
    msg.target = this;
    // 当设置这个值时,Looper会优先处理这个消息
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 添加到队列中
    return queue.enqueueMessage(msg, uptimeMillis);
}

4、总结

Handler的工作机制已经讲解完成,来总结一下:
每个线程对应一个Looper对象,创建Looper对象时会创建一个消息队列MessageQueue,Looper的工作是循环从MessageQueue中取出Message对象,并交给Message对象指定的Handler来处理。用户可以使用Handler对象向消息队列发送消息。下面给出了上面分析类的类图。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值