注:本文参考于某公开课,如有侵权,请联系本人,会立即删除
注:仅用作自我学习记录,未有任何商业用途。
我们都知道handler是安卓的消息传递机制,使用handler可以实现多线程通信。还有常说的handler四大成员:handler、message、messageQueue、looper,及其功能:
Message:封装需要传递的消息,可以传递数据;
MessageQueue:消息队列,但是它的内部实现并不是用的队列,而是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
但是,android系统是由Java封装的,那为什么还要在Java已经实现了多线程通信的前提下,增加自己的多线程通信机制呢?
这就要从Java的多线程通信说起。
Java中实现多线程之间相互通信访问数据有很多方法,常见的有:
1.通过关键字“syncheronized”给对象上锁的方法实现线程间的通信:即多个线程持有同一个对象,他们都访问同一个“共享变量”,但是同一时间只有拿到对象锁的线程,可以访问“共享变量”,其他线程只能等待对象锁被释放才有可能获得对象锁,从而去访问“共享变量”。
2.使用Object的wait/notify机制,当object调用wait()方法后,该object所在线程进入阻塞状态,直到其他线程调用notify(法,object所在线程才被唤醒。
这些方法都绕不开一个共同的特点:阻塞!!!
作为鲜明的特点,我们知道:
1. android系统是由JAVA封装的。
2. android将app启动时运行的线程称为主线程或UI线程。
3. 耗时操作(网络请求、数据库操作等)都要放到子线程(又叫工作线程)中去,不能在UI线程(又叫主线程)中执行(防止UI线程阻塞,否则会ANR:这里的阻塞是指,UI线程长时间去执行某个耗时操作,而无法响应用户的操作,导致的ANR)。
4. 只有UI线程可以更新UI。
那现在就需要线程间的通信了:UI线程不能执行耗时任务,要交给子线程,子线程执行完任务后,又不能把结果直接反馈的UI上,需要先把结果给UI线程,由UI线程去更新UI。如下图:
android中实现多线程通信的方法有:
1. view.post(Runnable runnable):通过view切换回UI线程
2. activity.runOnUiThread(Runnable runnable):通过activity对象的引用切换回UI线程
3. AsyncTask:内部封装了UI线程和工作线程的切换操作
4. Handler机制。
总结:由于android的特殊性,创建了UI线程;由于UI线程的特殊性,创建了许多工作线程和UI线程通信的方法,handler就是其中一种“android多线程通信机制”。
Handler
handler按其特点理解,提供的方法大体无非就三种作用:发送消息、处理消息、切换线程。
1.发送消息:
· boolean sendEmptyMessage(int what);
· boolean sendEmptyMessageAtTime(int what,long time);
· boolean sendEmptyMessageDelayed(int what,long delay);
· boolean sendMessage(Message msg);
· boolean sendMessageAtTime(Message msg,long time);
· boolean sendMessageDelayed(Message msg,long delay);
2.处理消息:
void handleMessage(Message msg);
3.切换线程:
· boolean post(Runnable r)
· boolean postAtTime(Runnable r, long time)
· boolean postDelayed(Runnable r,long delay)
(注:其实这里的切换线程,只是说对应的事务操作不用回到创建handler的地方,在重写的handleMessage中处理,而是直接在runnable中重写的run方法执行而已。)
说明:
以上方法总中参数“AtTime”和“Delayed”都是毫秒值,不同的是:前者的时间参数是指从系统开机时间算起的“time”毫秒后;而后者是从当前时间开始算起的“delay”毫秒后。
SystemClock.updateTimeMillis()可以获取系统开机到现在的毫秒数。
到此,handler机制的中的1/4成员handler基本介绍完了,是不是想说这些我都知道,哈哈,和你一样,我也就知道这么多。回归正题,总结一波:
1. handler的作用无非就是发送消息、切换线程、处理消息。
2. 在使用中,我们在哪个线程中创建了handler实例,哪个线程就是这个handler的“载体”、消息的接收端:在其他线程调用本线程的handler的sendMessage发送消息,那这条消息都会在本线程重写的方法handleMessage中接收到。
3. handler接受消息端是线程独立的,只有实例化本Handler的线程接受消息;但handler发送消息端多线程共享的:即拥有本Handler的引用的线程都可以作为消息的发送端,调用handler的发送消息方法来发送消息。所以,handler不是线程独立的。
所以,Handler需要一个独立存在于线程内部且私有使用的类来帮助它接受消息!
对,就是Looper!由此,Looper的作用就是辅助Handler接收消息且独立于线程内部。
那么问题又来了,如果同时有多个线程通过handler的引用向该handler发送消息,那么Looper怎么同时处理这些消息呢?
于是,为了防止多线程同时发消息Looper忙不过来,又设计了一个MessageQueue类,以队列的方式保存着Looper从其他线程接收到且未来得及发送给handler处理的消息,这样,Looper就可以一个个的有序的从MessageQueue中取出消息处理了。
可见,设计MessageQueue是为Looper服务的,Looper是线程独立的,那么MessageQueue也是线程独立的。
至此,handler四大成员全部亮相了。
小结:
1.Handler的引用是多线程共享的。
2.Message:提供的消息的标准化封装,视为消息的载体
3.MessageQueue:以队列的形式保存着待处理的消息
4. Looper:消息接收端,负责不断从MessageQueue中取出消息分发给handler接受消息端。
为了规范消息传递的格式定义了Message。
为了实现消息接收端值存在于线程内部私有化使用定义了Looper。
为了解决多线程同时发送数据Looper发来消息处理时会产生的问题而设计了MessageQueue。
这里关于消息接收端的理解(要区别消息的接受和处理时不同的概念):
对外:handler的实例化所在的线程即为消息的接收端;在handler内部,实际是looper接受了消息,并保存到了消息队列中,然后通过不断循环,按顺序一个个取出,交给handler去处理,即执行 void handleMessage(Message msg); 方法。
总结一下,四大成员的关系:
1.一个线程可以有多个Handler,而对于Handler来说,一个Handler只能实例化在某一个线程中,但其引用可以在任意多个线程(即前面说到的消息的发送端有多个,接收端只有一个)。
2. 一个线程只有一个looper,一个looper又唯一对应一个MessageQueue。
下一篇讲Message源码