Handler原理

1.handler基本信息:

定义:一套 Android 消息传递机制。

作用:在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理。

使用Handler的原因:将工作线程需操作UI的消息 传递到主线程,使得主线程可根据工作线程的需求 更新UI,从而避免线程操作不安全的问题。

消息(Message):

定义:线程间通讯的数据单元(即Handler接收&处理的消息对象)

作用:存储需操作的通信信息

消息队列(Message Queue):

定义:一种数据结构(存储特点:先进先出)

作用:存储Handler发送过来的消息(Message)

处理者(handler):

定义:主线程与子线程的通信媒介 线程消息的主要处理者

作用:添加消息(Message)到消息队列(message Queue)处理循环器(Looper)分派过来的消息(message)

循环器(Looper):

定义:消息队列(Message Queue)与处理者(Handler)的通信媒介

作用:消息循环,即消息获取:循环取出消息队列(Message Queue)的消息(message)消息分发:将取出的消息(Message)发送给对应的处理者(Handler)

2..Handler使用方式 因发送消息到消息队列的方式不同而不同

共分为2种:使用Handler.sendMessage()、使用Handler.post()

2原理


通过handler.sendMessage这个方法,把当前的消息对象message传入到handler中,通过handler中的messagequeue对象的引用(通过在handler的构造函数中,用looper获取到的),把Message放入到MessageQueue中。在message存放的时候,message对象的target属性记录了当前的handler。message通过消息的执行时间when,从小到大排列插入到消息链表中。我们looper的loop方法中的for(死循环)去查看当前的MessageQueue链表中是否有需要执行的Message。通过Message的when(消息的执行时间判断)和当前的系统时间戳做对比,如果当前的系统时间戳小于当前messagequeue链表中的消息执行时间,当前的执行进入等待状态。(管道机制,nativepollonce)如果当前的系统时间戳大于或等于当前的MessageQueue链表中的消息执行时间,我们就把当前的message从消息链表中删除,并且把该消息返回给Looper的loop方法中。在loop方法中获取到message以后判断message所对应的target(也就是发送message的handler)是否存在,如果存在就调用target.dispathMessage方法把当前的message

传入,在dispathMessage方法中,我们将调用handleMessage这个方法。把当前取出的message传出去。这样在我们handler中重写的handleMessage就拿到了当前处理的消息。

4.Handler机制的工作流程主要包括4个步骤:

(1).异步通信准备:

具体描述:在主线程中创建:处理器对象(Looper)消息队列 对象(Message Queue)Handler 对象

(2).消息发送

具体描述:工作线程通过Handler发送消息(Message)到消息队列(Message Queue)中

(3).消息循环

具体描述:消息出队:Looper循环取出消息队列(Message Queue)中的消息(Message)消息分发:Looper将取出的消息(Message)发送给创建消息的处理着(Handler)

(4).消息处理

具体描述:处理者(Handler)接收处理器(Looper)发送过来的消息(Message)处理者(Handler)根据消息(Message)进行UI操作

5.当创建Handler对象时,则通过 构造方法 自动关联当前线程的Looper对象 & 对应的消息队列对象(MessageQueue),从而 自动绑定了 实现创建Handler对象操作的线程

创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象

即 主线程的Looper对象自动生成,不需手动生成;而子线程的Looper对象则需手动通过Looper.prepare()创建

6.在子线程若不手动创建Looper对象 则无法生成Handler对象

根据Handler的作用(在主线程更新UI),故Handler实例的创建场景 主要在主线程

消息循环的操作 = 消息出队 + 分发给对应的Handler实例

分发给对应的Handler的过程:根据出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现 消息处理 的操作

特别注意:在进行消息分发时(dispatchMessage(msg)),会进行1次发送方式的判断:

若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()

若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg)

7.线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

8.ThreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。

ThreadLocal的静态内部类ThreadLocalMap为每个Thread都维护了一个数组table,ThreadLocal确定了一个数组下标,而这个下标就是value存储的对应位置。。

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

在进行get之前,必须先set,否则会报空指针异常;如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。 因为在上面的代码分析过程中,我们发现如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。

9.handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,而且消息队列和handler都是依赖于同一个线程的。

send方案发送消息(需要回调才能接收消息)

1、sendMessage(Message) 立即发送Message到消息队列

2、sendMessageAtFrontOfQueue(Message) 立即发送Message到队列,而且是放在队列的最前面

3、sendMessageAtTime(Message,long) 设置时间,发送Message到队列

4、sendMessageDelayed(Message,long) 延时若干毫秒后,发送Message到队列

post方案 立即发送Message到消息队列

1、post(Runnable) 立即发送Message到消息队列

2、postAtFrontOfQueue(Runnable) 立即发送Message到队列,而且是放在队列的最前面

3、postAtTime(Runnable,long) 设置时间,发送Message到队列

4、postDelayed(Runnable,long) 在延时若干毫秒后,发送Message到队列

10.handler机制中包含4个关键类(下面对源码的解析也是从这4个类入手),Message(消息),MessageQueue(消息队列),Looper(轮询器),Handler(消息发送和接收并处理),简单一句话概括就是:handler负责发送message,将其加入到MessageQueue中,Looper不间断的从MessageQueue中取出消息,并发送给对应的handler实例去处理.

11.主线程与子线程

主线程((Ui线程,Main.thread):

定义:当应用程序第一次启动时,会同时开启1条主线程。

作用:处理与UI相关的事件(如更新操作等)。

子线程(工作线程):

定义:人为手动开启的线程。

作用:执行耗时操作(如网络请求、数据加载等)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342