1.主线程不能进行耗时操作 (主线程进行耗时操作会导致应用ANR)
2.子线程不能更新UI(子线程更新UI会导致CalledFromWrongThreadException)
二、延伸
系统为什么不允许在子线程中访问UI呢?这是因为Android的UI控件是线程不安全的,如果多线程并发会导致UI控件处于不可预期状态。所以耗时操作要在子线程中进行,更新UI要在主线程中进行,而系统提供Handler就是为了解决在子线程中无法访问UI的矛盾。
三、原理
同一个进程不同线程用的是同一片内存空间,让其他线程要处理的消息放在这个特定的内存空间上,处理消息的线程来这个特定的内存空间取消息。扮演这个特定的内存空间就MessageQueue。
四、分析
Handler的主要工作包含消息的发送和接收过程。Handler创建时会采用当前的线程的Looper来构建内部的消息循环系统,如果当前线程没有Looper就是报错(can't create handler inside thread that has not called Looper.prepare())。Handler通过post的一系列方法以及send的一系列方法来实现,post的一系列方法最终通过send的一系列方法来实现。
ThreadLocal可以在指定的线程中存储数据并只有在指定的线程中才可以获取存储的数据。
Looper,线程中默认是没有Looper的如果需要使用Handler就必须为线程创建Looper。主线程会在被创建时初始化Looper。Looper可以通过Looper.prepare()来创建,并通过Looper.loop()来开启消息循环。prepare() 可以创建looper并将looper对象保存在ThreadLocal中。Looper也可以退出,quit会直接退出Looper,而quitSafely把消息队列中已有的消息处理完毕后才安全退出。Looper最重要的方法是loop(),loop()是一个死循环唯一跳出循环的方式是MessageQueue 的next方法返回了null,loop()通过调用MessageQueuede 的next方法获取新消息,而next是一个阻塞操作,如果没有消失时next会一直阻塞在那里。也导致loop也一直阻塞。当有消息时Looper就会出来这条消息通过调用dispatchMessage将消息交个Handler,最后回收消息。
Handler处理消息首先检查Message的callback是否为null,不为null就通过handlerCallback来处理消息。Message的callback是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数。
MessageQueue是一个消息存储单元,以队列的形式对外提供插入和删除的工作。采用单链表的数据结构存储消息。(单链表插入和删除有优势)enqueueMessage作用是往消息队列中插入一条数据,next是从消息队列中取出一条消息并移除。next是一个无限循环的方法,如果消息队列中没有消息,那么next会一直阻塞。如果有新消息来时,next会返回这条消息并从链表移除。
五、具体
在子线程中请求完网络 首先通过Message message = Message.obtain(); 获取一个message对象,而不是new一个因为Message.obtain()内部采用线程池的方式,所以可以提升效率。Handler的构造方法会通过ThreadLocal获取Looper对象,Looper会在构造方法中创建一个消息队列MessageQueue,所以可以通过looper获取消息队列(主线程会在创建的时候创建的时候初始化Lopper,并将looper保存在ThreadLocal中,还会开启循环调用Looper.loop的方法)可以把需要传的内容放在Message里然后通过sendMessage方法发送消息然后调用Handler的enquequeMessage,然后调用MessageQueue的enqueueMessage方法将消息通过msg.tag 的方式将handler插入消息队列,如果消息队列中没有消息或者消息触发的时间在列表的头结点之前,将消息插到头节点。如果没有则死循环,找到当前消息比链表中的消息早发生的消息,插入到那条消息前面,否则就插入到链表表尾。loop方法里获得Looper对象的消息队列,然后是一个死循环,(调用MessageQueue的next方法获取消息,next方法也是一个死循环,如果没有消息一直阻塞,如果有消息返回这条消息并从链表中移除。)loop方法取得消息通过dispatchMessage分发消息去处理最后回收消息。