专栏首页木溪知识加油站笔记——安卓消息机制Handler(十六)

笔记——安卓消息机制Handler(十六)

——个人平时笔记,看到的同学欢迎指正错误,文中多处摘录于各大博主与书籍精华

1、定义:Android的消息机制主要是指Handler的运行机制,Handler并不是专门用于更新UI的,它只是常被开发者用来更新UI,是同一个进程中线程间的通信机制,能够将一个任务切换到指定的线程中去执行。

Handler消息机制流程(深入探讨Android异步精髓Handlerhttps://blog.csdn.net/lfdfhl/article/details/53332936#commentBox):每一个handler的创建都必须有Looper.prepare()->new Handler()-> sendMessage()->MessageQueue->Looper.loop()->handlerMessage(),但是在UI线程是主线程中,系统会自动调用Looper.prepareMainLooper()方法创建主线程的Looper(Looper.prepare()与Looper.loop())和消息队列MessageQueue。

>1、Looper.prepare():创建一个Looper。在其内部源代码中,每一个Looper.prepare()初始化一个MessageQueue消息队列和一个线程Thread,这也是为什么很多人说的一个Handler只能持有一个MessageQueue的原因。并且由源码可知一个线程对应一个Looper也只有一个Looper.prepare(),否则会抛出异常。在prepare()内会调用sThreadLocal.set(new Looper(quitAllowed)),至于Looper,它在Android的消息机制中担负着消息轮询的角色,它会不间断地查看MessageQueue中是否有新的未处理的消息;若有则立刻处理,若无则进入阻塞

>2、sendMessage():调用handler.sendMessage()等方法发送消息,在其内部源码中都会调用enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis)方法,并在其方法内部处理后调用queue.enqueueMessage(msg,uptimeMillis)方法将消息插入到消息集MessageQueue队列中,并且MessageQueue队列是遵从先进先出的原则。但是有个例外,如果调用handler.sendMessageAtFrontOfQueue()方法会直接将uptimeMillis入队列的延迟时间设置为0,所以会直接将msg插入至消息队列头部。待取出消息时优先头部取出的。如果说为什么调用sendMessage()能够准确的发送到对应的handlerMessage()接收,那是因为在enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis)方法内部已经为每一个msg指定了target标签,原文“msg.target =this;”。

>3、queue.enqueueMessage(msg, uptimeMillis):将消息发送插入到MessageQueue消息队列中,uptimeMillis是发送的延迟时间。MessageQueue消息队列它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表(单链表在插入和删除操作上效率比较高)。

>4、Looper.loop():消息的出队执行者,在loop()内部会发起一个死循环不断遍历MessageQueue内部轮询,取出消息Message msg =queue.next()next()取出一条消息并将其从消息队列中移除,直到取出的消息不为空时,才调用msg.target.dispatchMessage(msg)将消息发送到熟悉的handleMessage(msg)中接收(而msg.target=this;this即Handler本身),并在dispatchMessage方法内采用回调彻底完成线程切换。在需要的时候或事情完成后调用quit()方法停止消息的轮询,此时next()会返回null,loop()方法会结束,Looper也跟着退出,Looper退出后线程也会跟着终止。

>5、Handler在哪个线程创建,也就是运行于哪个线程,Handler的主要作用是将一个任务切换到某个指定的线程中去执行;当handler通过一系列的post或send方法发送消息到达目标线程的MessageQueue(消息队列是指定的目标线程持有的)则此时也就切换了线程。线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper。参考:android之handler切换线程终极篇

>6、ThreadLocal并不是线程,是一个数据存储类,它的作用是可以在每个线程中存储数据。Handler创建的时候会采用当前线程的Looper来构造消息循环系统,而ThreadLocal可以在不同的线程中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。ThreadLocal<T> mThreadLocal = new ThreadLocal<T>();mThreadLocal能够存储当前自己线程下的值,多个线程间的数据不干扰。不同线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的value值。很显然,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本并且彼此互不干扰。

小结: 1.一个线程对应一个Looper 2.一个Looper对应一个MessageQueue消息队列 3.一个线程对应一个MessageQueue消息队列 4.线程,Looper,MessageQueue消息队列三者一一对应 5.post的一系列方法最终也是通过send的一系列方法来实现的如:handler.postAtTime(),handler.sendMessageAtTime() 6.一个线程可以有多个Handler

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java基础系列(十一):注释

    曾经看到过一句话:“我最烦的就是写注释和看不写注释的代码”,也许有玩笑的成分的在,但是不可否认的是,注释对于代码来说,是必不可少的,它可以大大的增加代码的可读性...

    Vi的技术博客
  • Java基础系列(四):控制流程

    和其他程序设计语言一样,Java使用条件语句和循环结构确定控制流程,在介绍这些条件语句和循环结构之前,我们先来了解一下块作用域这个概念。

    Vi的技术博客
  • Java 基础系列(一):基础数据类型

    今天我们来聊一下Java这门语言的数据类型,众所周知,Java是一种强类型语言。在Java中,一共有8种基本类型,其中4种整形,2种浮点类型,1种用于表示Uni...

    Vi的技术博客
  • Java基础系列(二):运算符

    计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:

    Vi的技术博客
  • Java基础专题(三):字符串

    从概念上来讲,Java字符串就是Unicode字符序列。例如,"Java\u2122" 由5个Unicode字符J,a,v,a,和 ™。Java没有内置的字符串...

    Vi的技术博客
  • 异常、堆内存溢出、OOM的几种情况

    【情况一】:    java.lang.OutOfMemoryError: Java heap space:这种是java堆内存不够,一个原因是真不够,另一个...

    业余草
  • Java基础系列(十二):继承

    Java中有一个非常重要的概念:继承。利用继承,我们可以通过一个已经存在的类构造一个新的类,继承已经存在的类就是服用这些类的方法和数据域,并且在原本类的基础上可...

    Vi的技术博客
  • Spring Boot 2.0 系列(一):快速开始

    Spring Boot可以使我们轻松地创建独立的、生产级的基于Spring的应用程序,由于整合了一些对Spring和第三方库的配置,我们可以快速开始一个应用程序...

    Vi的技术博客
  • Java基础系列(五):数组

    在Java中,有一种数据结构叫做数组,它用来存储同一类型的值的集合。通过一个整型下标可以访问数组中的每一个值。例如,如果a是一个整型数组,那么a[i]就是数组中...

    Vi的技术博客
  • Java基础系列(十):对象构造

    那么就会自动地赋为默认值:数值为0,布尔值为false,对象为null,但是我们一般不建议这么做,这样会大大的增加了阅读程序的难度,比如说上面的程序中,name...

    Vi的技术博客

扫码关注云+社区

领取腾讯云代金券