Android消息机制(1)----简单介绍
什么是Handler?
Handler是Android消息机制的上层接口,所以开发者在开发过程中只需要和Handler交互即可。Handler的使用过程很简单,通过它可以轻松的将一个任务切换到Handler的所在的线程中去执行。很多人认为Handler的作用是更新UI,这的确没错,但是本质上来说,Handler并不是专门用于更新UI的,他只是常被开发者用来更新UI。
Handler的消息机制
Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue的中文翻译是消息队列,顾名思义,它的内部存储了一组信息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper的中文翻译为循环,在这里可以理解为消息循环。由于MessageQueue是一个消息队列,他不能去处理信息,所以引入Looper这个东西来填补功能。Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待MessageQueue中的消息,处于一个等待状态。ThreadLocal是Looper中的一个新的概念,ThreadLocal并不是一个线程,它的作用是可以在每个线程中存储数据。
在Handler创建的时候会采用当前线程的Looper来构造消息循环系统,那么Handler内部如何获取到当前线程的Looper?这时候就要使用ThreadLocal,ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过这个ThreadLocal我们可以获取到每个线程的Looper。Handler的运行机制、MessageQueue的工作流程、Looper的工作流程这三个东西其实是一个整体。Handler的主要作用是将一个任务切换到某个指定的线程中执行。
注意:线程默认是没有Looper的,如果我们要使用Handler就要为线程创建一个Looper。但是有一个线程不同,那就是主线程(UI线程),就是ActivityThread,在主线程ActivityThread创建时,就会自动初始化Looper,所以我们在主线程中可以默认的使用Handler的原因。
为什么不能在子线程更改UI
对于每一个学Android的人,都会经历一个共同的问题,那就是为什么Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常。下面说一下原因:
在View的源码中,我们知道绘制View是从ViewRoot来开始的,而ViewRoot对应于ViewRootImpl类,ViewRootImpl中不仅对UI进行操作,而且还对UI操作进行了验证,这个验证工作是由ViewRootImpl的checkThread方法来完成,如下:
void checkThread(){
if(mThread != Thread.currentThread()){
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views."
)
}
}
为什么要进行这个验证呢?
- Android的UI线程是线程不安全的,如果在多线程中并发访问可能会导致UI控件处于一个不可预期的状态。
- 非线程安全不能加Lock线程锁,否则会阻塞其他线程对View的访问,效率就会变得低下。
- 主线程负责更新,子线程负责耗时操作,能够大大提升响应效率。
- 主线程进行耗时操作,很容易导致程序无法响应即ANR问题。
所以综上,我们要采用单线程模型来处理UI操作,对于开发者来说,我们就可以通过Handler切换一下UI的访问线程。
Handler的简单工作流程
在Handler创建完毕后,其内部的Looper和MessageQueue就可以和Handler协同操作了。
通过Handler的post方法将一个Runnable投递到Handler内部的Looper中心去处理,也可以通过Handler的send方法来发送一个信息,来让这个消息也同样会在Looper中处理。当Handler的send方法被调用时,它会调用MessageQueue的enqueueMessage方法将这个消息放入队列中,然后Looper发现有新消息到来,就会处理这个消息,最终消息中的Runnable或者Handler的handleMessage方法就会被调用。由于Loop是运行在创建Handler的线程当中,所以Handler的业务逻辑就被切换到创建到Handler的线程中去执行了。