本节主要是对handler机制有一个大概的了解,后面的章节还会详细的分析handler机制。
handler产生背景
在介绍handler之前先来介绍下它产生的背景,只有了解了背景才能加深对它的了解。
子线程与主线程通信
子线程与主线程通信,那肯定是一些操作是不能在子线程中进行的,只能在主线程进行。那就从哪些操作需要在主线程中执行说起吧。
ui的绘制/更新只能在主线程
android中ui的绘制/更新只能在主线程中,为啥会有这样的一个规定呢?主要的原因是为了ui绘制/更新的”安全性,一致性“。
假如在子线程中也可以进行ui的绘制/更新,那会有什么问题呢?比如当前有2个子线程:线程A,线程B,同时分别把TextView的内容设置为”hello A“,”hello B“。那这时候界面上显示的TextView的内容就不确定了,有可能显示”hello A“,也有可能显示”hello B“。造成这个的主要原因是:两线程同时设置,并且这俩线程先后执行的顺序是不确定的。
那应该有人就会说,在为TextView设置text的时候,增加同步锁(synchronized)不就可以解决问题了吗?是的这样确实可以解决问题,但又引入了别的问题:绘制效率问题,复杂度问题,容易出现死锁问题等。
绘制效率问题:因为有了锁,那等待,释放锁,线程之间的切换都是开销,这会严重的影响绘制效率。
复杂度问题:整个View体系相应的改变View属性的地方都需要加synchronized,有漏掉的地方就会出现绘制出错的问题。
容易出现死锁问题:因为有了同步机制,那死锁的概率就会大大增加啊,死锁的问题有多严重大家都清楚,界面上会出现卡顿,甚至没办法执行绘制的情况。
所以基于以上的这些问题,ui的绘制/更新在主线程中执行这是最简单有效的方法。
主线程不能有耗时任务
像上面提到的ui的绘制/更新需要在主线程中执行,还有点击事件的分发,四大组件的生命周期方法执行等都是需要在主线程里面执行的。如果主线程里面存在耗时的操作,那产生的问题就大了,比如在Activity的onCreate方法中有耗时操作,那就会影响后面的生命周期的执行,站在用户角度来看的话,整个界面显示很慢,长时间处于白屏状态,这肯定不是一个好的用户体验。
因此耗时的任务就需要放在子线程里面执行(比如网络请求,读取本地文件等等),当子线程执行完毕后,就需要把需要更新view的数据发送给主线程,让主线程帮忙绘制view。
binder线程与主线程通信
在启动Activity时候,AMS(ActivityManagerService简称)会通过binder调用把消息发送给app端,这个时刻是运行在binder线程中的,那后续Activity的初始化,启动等流程肯定是不能继续在binder线程中的,为啥不能继续在binder线程中?主要原因是binder线程的作用是处理binder相关的操作,既然binder操作已经完成了那肯定就需要赶紧把binder线程“解放”出来,让它继续处理其他的binder操作;如若不把binder线程”解放“出来,会影响app端binder的服务,同时Activity启动及四大组件相关的启动操作都会变的复杂,因为需要涉及到”安全性,一致性“相关的问题。
上面提到的子线程与主线程通信和binder线程与主线程通信,这只是线程之间通信的其中两个场景,还有很多其他的场景(比如业务内也会有线程之间通信),android设计者,为了开发者更加便利,不用重复的造轮子。因此在这样的背景下设计了handler,handler目的是解决线程之间通信问题。
handler原理
生产者消费者模式
先来介绍下生产者消费者模式,为啥要介绍这个模式呢,因为handler的核心原理就是生产者消费者模式。
上图展示的是生产者消费者模式,生产者把数据放入数据缓存区中,消费者从数据缓存区中取数据,生产者可以有多个,消费者也可以有多个,生产者与消费者一般都位于不同的线程。
生产者消费者模式可以衍生出多种结构:多个生产者和多个消费者(消费者处于不同的线程,多个生产者和一个消费者,一个生产者和一个消费者等。
生产者消费者模式实现线程之间通信
最主要是基于共享内存这个机制,一个进程内的内存在线程之间是共享的。
上图的结构是多个生产者和一个消费者的结构,展示了是如何实现线程之间通信的,下面对上图进行介绍:
数据队列:它是线程之间共享的,它主要的目的是存放数据,数据读取器从中拿数据。因为数据队列是多线程之间共享的,因此需要使用同步机制来保证它的安全。
数据读取器:这是我自己取的一个名字,它的主要作用是不断的循环从数据队列中拿数据,拿到数据后交给处理者处理数据。
处理者:这也是我自己去的一个名字,它的主要作用就是处理数据,是生产者消费者模式中的消费者角色。
消费者线程:数据队列,处理者,数据读取器都在这个线程内执行。
当数据队列中没有数据的时候,消费者线程进入wait状态并且释放掉cpu等资源。
当线程1的生产者把数据放入数据队列后,会把处于wait状态的消费者线程唤醒,这时它的数据读取器从数据队列中取到刚刚的数据,再把数据交给处理者处理。这个过程就实现了线程1与消费者线程之间的通信。
handler的核心原理就是生产者消费者模式,基于此基础上进行封装,封装出了Looper,Handler,Message,MessageQueue这些类,那就来介绍下这几个类。
handler核心类
Looper:与上面的数据读取器的功能类似,它的作用就是从MessageQueue中取出Message交给Handler处理,Hanlder处理完毕后,再不断的接着这样的循环此过程。
MessageQueue:它就是上面的数据队列,它的作用就是用来存放Message,以链表结构存放Message的,并且为Looper提供Message,当队列中没有Message的时候线程进入wait状态;当往队列中存放Message后,MessageQueue所在的线程被唤醒,获取到Message交给Looper处理。
Message:与上面的数据相对应,线程之间通过handler通信,传递的数据就是Message
Handler:它就是消费者,与上面的处理者相对应,它的作用是在它对应的线程内处理Message。
总结
本节主要介绍了handler的产生背景和handler的原理,这节只是一个概览,handler的知识是很多的,在后面的章节中会一一介绍,下面用一张图来做一个总结