Android Handler机制

        Android开发中经常用到handler来发送消息,比如将消息抛到主线程去更新UI。handler机制涉及到四个核心的类:

  • Looper:消息循环,有一个Messagequenue,不断从消息队列中取出消息;
  • MessageQuenue:消息队列,里边包含Message;
  • Message:消息,里边有一个Handler,负责处理该消息;
  • Handler:管理消息队列,里边有Looper和MessageQueue;

       

        为了更好的理解这三者的关系,我们可以想象成现实生活中,工厂里边的一条流水线。有一个放产品(Message)的传送带(MessageQuenue),被机器带动起来不断循环运行(Looper),而工人(Handler)需要将产品放到传送带上,然后传送到指定位置时再将产品进行下一步处理。

        从上述表达的关系可以看出,有一个MessageQuenue,可以往里边仍Message,而Looper则要让MessageQuenue转起来,不断从里边取消息;而Handler负责把Message放到队列里边,最后再处理这个消息。

注:以下源码基于Android 9.0(版本名称:  Pie     API Level:  28)。

Looper类分析
frameworks/base/core/java/android/os/Looper.java    
在该文件中给我们提供了一个实例:
 

  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          // ①调用prepare
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *          // ② 进入消息循环
  *          Looper.loop();
  *      }
  *  }</pre>

上述使用了Looper的两个关键调用(①和②),下面逐一分析。

Looper准备:

    public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        // 一个Looper只能调用一次prepare
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 构造一个Looper对象,设置到调用线程的局部变量中
        sThreadLocal.set(new Looper(quitAllowed));
    }
    // sThreadLocal定义
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal是一个泛型,是Java线程中的局部变量。全名是Thread Local Variable。具体可以自行研究源码:libcore/ojluni/src/main/java/java/lang/ThreadLocal.java

Looper构造函数:

    private Looper(boolean quitAllowed) {
        // 构造一个消息队列
        mQueue = new MessageQueue(quitAllowed);
        // 获取当前线程的Thread对象
        mThread = Thread.currentThread();
    }

prepare主要创建了Looper对象,并保存在调用线程的ThreadLocal中。同时Looper对象内部封装了一个消息队列。prepare通过ThreadLocal机制将Looper和调用线程关联。Looper循环:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        // 返回保存在调用线程的TLV中的Looper对象。
        final Looper me = myLooper();
        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
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // ... 细节省略
            try {
                // 调用该消息的Handler,交给它的dispatchMessage函数处理
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            // ... 细节省略
            msg.recycleUnchecked();
        }
    }

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     *      
     */
    public static @Nullable
    Looper myLooper() {
        return sThreadLocal.get();
    }

通过上面源码分析可以了解到Looper的作用:

  • 封装一个消息队列(Messagequenue);
  • Looper的prepare函数将Looper和调用prepare的线程(即处理线程)绑定;
  • loop函数启动一个无线循环来处理消息队列中的消息。

        当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。可事件源又是怎么向Looper消息队列添加消息的呢?下面需要了解Looper、Message、Handler的关系。

  • Looper中有一个Message队列,里边存储的是一个个待处理的Message;
  • Message中有一个Handler,这个Handler是用来处理Message的。

其中Handler类封装了很多琐碎的工作。具体:

frameworks/base/core/java/android/os/Handler.java

    final Looper mLooper;    // 有一个Looper
    final MessageQueue mQueue;    // 有一个消息队列
    final Callback mCallback;    // 有一个回调用的类
    final boolean mAsynchronous;  // 异步相关设置,本文不做分析

Handler有几个构造函数,主要区别是对上面三个重要成员变量初始化上。

    // 构造函数1
    public Handler() {
        this(null, false);
    }

    // 构造函数2
    public Handler(Callback callback) {
        this(callback, false);
    }
    // 构造函数3
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    // 构造函数4
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    // 构造函数5
    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(Callback callback, boolean async) {
        // ... 细节省略
        // 获得调用线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 得到Looper的消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

        上述构造函数中,Handler中的消息队列变量最终都会指向Looper的消息队列,Handler提供了一系列函数,帮助我们完成创建消息和插入消息队列的工作。这里列出一部分分析:

// 查看消息队列中是否有消息码是what的消息
public final boolean hasMessages(int what)
// 从Handler中创建一个消息码是what的消息
public final Message obtainMessage(int what)
// 从消息队列中移除消息码是what的消息
public final void removeMessages(int what)
// 发送一个只填充了消息码的消息
public final boolean sendEmptyMessage(int what)
// 发送一个消息,该消息添加到队列尾
public final boolean sendMessage(Message msg)
// 发送一个消息,该消息添加到队列头,所以优先级很高
public final boolean sendMessageAtFrontOfQueue(Message msg)

        以sendMessage为了,其代码实现如下所示:

    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 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) {
        // 把Message的target设为自己,然后加入到消息队列
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler的消息处理

        刚才,我们往Looper的消息队列中加入了一个消息,按照Looper的处理规则,它在获取消息后会调用target的dispatchMessage函数,再把这个消息派发给Handler处理。

    public void dispatchMessage(Message msg) {
        // 如果Message本身有callback,则直接交给Message的callback处理
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 如果本Handler设置mCallback,则交给mCallback处理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 最后才交给子类处理
            handleMessage(msg);
        }
    }

        通常情况下,我们一般都是采用第三种方法,即在子类中通过重载handleMessage来完成处理工作。

        要理解Handler机制,主要是弄清楚Looper、MessageQuenue、Message、Handler之间的关系。为了便于理解,我们可以把这套框架想象成现实生活中工厂流水线,工人(Handler)拿到产品(Message),放在传送带(MessageQuenue)上,同时做了一个标记(msg.target = this;),传送带由机器带动(Looper),然后当机器将产品传递到另一位置时,该员工根据产品上的标记,负责处理该产品,分发到不同的地方。

        上述分析细节可以自行查看Android源码,推荐网站:http://www.androidos.net.cn/sourcecode

扩展:

        Looper类里边启动了一个for(;;)循环,如果消息队列时会不会一直占用CPU时间片?

        循环中queue.next()函数里边用到了pipe/epoll机制,确保不会占用CPU时间片。之后会通过源码进行分析。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值