Handler 工作原理和源码解析

        在Android 开发中 有这样一个最基本的场景,当我们通过网络请求拿到服务端返给我们的数据后,我们需要把数据放到我们的页面上,一般网络请求的耗时操作我们都会在子线程中完成,如果这个时候你直接 去更新界面 就会报错,告诉你当前线程不是主线程无法更新界面操作,这个时候身边的老程序猿就会告诉你用Handler。

       那么什么是Handler 呢 ,简单的来讲其实就是Android 用来进行线程间通信的 ,如上面的例子 把子线程的消息发送给主线程 让主线程去完成相应的操作。具体的Handler 是怎么使用的这里不做描述,代码很简单网上例子也很多。我们主要讲讲原理。

      我们在使用Handler的时候一般使用的就两个方法,sendMessage()发送消息 和handleMessage() 分发消息并做相应的处理。我们可以进入Handler内部看看源码。看源码的时候我们首先看Handler的属性,


    final MessageQueue mQueue;
    final Looper mLooper;

我们拿出主要的两个属性,MessageQueue  消息队列,也就是说Handler 处理消息的方式是使用了消息队列,了解队列这个数据结构的就知道他的特性就是 FIFO (First Input First Output) 先进先出。也就是说 handler.sendMessage 的时候 就是把message 入队列。看看Handler源码中 发送消息的方法 是不是这样的逻辑。

我整理了一下 Handler 中涉及发送消息的方法

从上面的图中我们看到所有调用的发送消息的方法 (包括send , post )方法最终调用的是 enqueueMessage 方法

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

而在enqueueMessage 方法中调用的 MessageQueue 消息队列的 enqueueMessage 方法

  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;

关于消息 队列 消息是如何入队的涉及到数据结构的内容不在这篇文章中描述,简单讲就是 for 一个死循环通过时间 when 找到这个消息的位置然后 添加进去,其中还涉及到挂起和唤醒等操作。

所以handler.sendMessage  等一系列发送消息的方法 就是把message 添加到消息队列中。

接下来我们看之前还有一个属性Looper ,Looper 的作用我们先看 Handler 构造函数

  mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;

handler 构造函数最终执行的 代码片段中 有这个,Looper.myLooper() 获取Looper 对象,如果null 会报错。意思是线程中如果Looper 没有实例化 那么Handler 就不能创建,也就是说要想创建Handler 必须先实例化Looper。而且 这段代码中  mQueue = mLooper.mQueue ;     MessageQueue 对象,我们也是从 Looper  中 获取的。接下来看看Looper 的源码

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;

Looper 属性中 有一个 ThreadLocal <Looper> 这个很重要,ThreadLocal 有点像 HashMap  也是以key ,value 的形式存储数据,在这里 ThreadLocal 的key 值就是 当前Looper 所在的线程 , value  就是Looper 对象。也就是说 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));
    }

之前说 创建Handler 之前要先Looper .prepare()   从这里也可以看出  一个线程只能创建一个 Looper  对象。不然会报错。

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

在Looper 构造方法中 实例化了一个 消息队列 和获取 当前线程 从这里我们可以得到一个 对应关系 Thread --->Looper---->MessageQueue 一个 线程只有一个Looper  控制着唯一的 MessageQueue 

那么Looper 是怎么控制这个MessageQueue 的呢

  public static void loop() {
        final Looper me = myLooper();
        
        final MessageQueue queue = me.mQueue;
       
        for (;;) {
            Message msg = queue.next(); // might block
           
            msg.target.dispatchMessage(msg);
            msg.recycleUnchecked();
        }
    }

上面是我删掉了部分代码的 Looper.loop()方法中的代码 从代码中我们看出 loop 就是拿到 消息队列 ,然后不停的循环这个消息队列 从消息队列中拿消息。 queue.next()就是消息队列的出队列方法。从消息队列中拿到消息后 调用 message 的 target 的 dispatchMessage 方法  。 这个target 是什么看看 Message 源码 

   /*package*/ long when;
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;
    

target  就是Handler  , 那么 是什么时候 我们吧Handler 放到Message  中去的呢。

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

再看 Handler 的发送消息的时候 msg.target = this  把消息和 Handler 绑在一起。同时Handler. dispatchMessage 方法调用的就是handleMessage 方法。也就是 从消息的发送 到消息的处理 handler 一直伴随着这个消息,这样一来即使这个消息队列中 有多个handler 送进来的消息 分发出去的时候也不会 搞乱。谁送进来的谁拿走。

所以Looper 的作用就是让这个线程的消息队列 转动起来 。而我们之前创建Handler 的时候说要先调用 Looper .prepare 方法 实例化 同时 还要 Looper. loop  让消息队列 转动起来。但是我们在 实际运用中在activity 中使用Handler 的时候并没有用到Looper 是为什么呢。这就和我们的主线程 有关,我们的主线程 也就是 UI线程在哪里,似乎没见过(在Android 源码中隐藏起来了)。

public static void main(String[] args) {
            Looper.prepareMainLooper();
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

这是ActivityThread 类中的main 函数 也是我们APP 程序的入口,每个程序都有main 函数,Android也不例外。在 main 函数中 我们看到了 Looper.prepareMainLooper  初始化 。同时 Looper.loop  转动起来,也就是说在程序已启动 我们的主线程就已经初始化了Looper 并且让 消息队列开始工作了,所以之后 我们在 activity 中使用handler 就不需要再做了。好了Handler 的原理就是这样,有什么疑问可以留言

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值