一.什么是Handelr
- Android SDK中用来处理异步消息的核心类
- 子现程可以通过Handler来通知主线程进行更新UI
二.Handler机制的核心类
- Handler
- Message
- Looper
- MessageQueue
1.Message:
在整个消息处理机制中,message又叫task,封装了任务携带的消息和处理该任务的handler。有这么几点需要处理:
(1)尽管Message有public 的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源
(2) message 如果只需要携带简单的int信息,请优先使用arg1和arg2来传递信息,这比用bundler更节省内存。
三.源码分析
1.handler在哪个地方使用在哪个地方点进去看
2.进入到sendMessage()
3.再进入到sendMessageDelayed()
4.进入sendMessageAtTime()
会看到调用的各种发送消息的方法,最终都会走到这个方法。
5.msg 里面有个成员变量拿到了handler的引用,最后一句代码进入消息队列
6.点进入MessageQueue中看
消息入队方法enqueueMessage(),一个线程就只有一个消息队列 ,这里的MessageQueue不是一个链表结构,其实它是一个链式结构,就是单链。
这里大致,就是根据延时时间把要插入的message插入响应的位置,如果延时时间为0,新消息就会插入到队头,如果延时时间不为0 就会和队列里的其他消息对比插入到应用位置。
既然有入队,对应就有出队,出队是在messageQuenue方法中的next()方法,就是把message消息从队列里面取出返还给looper
7.点进looper看
looper 中拿出消息,有可能会堵塞,当我们队里里面没有消息,或者message的延时时间没到,他就会进行堵塞。
上面说过msg 的target保存的是handler 的引用,所以这句话调用的是handler 的dispatchMessage()方法,然后我们回到handler 看看这个方法。
8.回到Handler类
然后看到数据的方法在dispatchmessage中最后调用了handlerMessage()
这个handelMessage是子类必须实现的方法,也就是我们创建handler 必须实现的方法。
9.回到我们创建的Handler子类
其实如果我们的message不拿handler 这个句柄的话也可以实现,我们也可以再创建一个类,把message传给这个类,像实现handlemessage方法一样,但是这样我们使用者就要学习更多的handelr 知识,不但要了解handler还要会使用新类和message,这就导致我们学习成本更高了,所以这也是一个设计思想,方便使用者更简单,更方便的调用,直接把message返回给handler引用,传入到handlerMessage方法,让子类去实现这个方法,
下面我们来具体的梳理下流程
1.创建Handler子类通过调用sendMessage() 发送消息
2.Handelr 最终调用messageQuene的enqueueMessage()将消息传入到消息队列中
3.Looper中的调用MessageQueue的next()方法将取出消息。
4.Looper中通过Message的成员变量target拿到Handler的引用,继而回调到Handler的 handelMessage()方法
5.消息回到我们创建的子类实现的HandelMessage()方法
四.但是现在有几个疑问
- looper 谁调用的
- sendMessage是在子线程中发送的消息,为什么最后handleMessage 又回到了主线程
- 为什么messagequen 只有一个
1.Looper中创建MessageQueue ,主线程中ActivityThread调用Looper。
我们看我们new Handelr() 里面调用了this(null,false),false 又是什么
代表callback 和async ,callback又是什么,我们回到dispatchMessage方法看
我们创建时 callback为空就会进入else方法,else里面又为空,就会走handlerMessage方法
再看Handler构造函数中调用Looper.myLooper(),
再点进ThreadLocal是什么东西
ThreadLocal是做线程间数据隔离用的,就类似我们多线程操作共享数据,需要有同步锁机制,我们就记住,ThreadLocal对象最后返回的是当前线程的Looper对象
最后回到handler 中看如果mLooper 为空就会抛出异常,因为还没有调用Looper.prepar(),下面 mQuene=mLooper.mQueue,消息队列实际是从Looper中返回的,他是在Looper里,再来看Looper.prepar()是干什么
点进prepare()
prepare() 如果不为空,抛出异常,提示一个线程只能有一个Looper ,为空则创建一个Looper ,ThreadLocal 操作和map很像通过set去放,通过get去拉,点击newLooper看
创建了一个MessageQuene
在回到Looper中看,刚才prepare传的是true,还有什么地方传false,原来prepareMainLooper().传的是false ,那prepareMainLooper是谁调用的
ActivityThread 类里面调用了这个方法,那为什么主线程会传prepare()会传false,因为主线程的looper 是不允许退出的 ,因为你退出了再发handler 消息谁来处理呢
再来看looper的构造函数为私有,也就是一个线程只有一个looper,每个线程也就有一个对应的looper ,一个looper 又只有一个meesageQuene 也就是一个线程里只有个messageQuen 一个looper
2.Handler 消息驱动
再来考虑一个问题,为什么looper 轮训器一直轮训messagequne缺没有造成阻塞
看for循环中有消息就立即处理,因此handler 是通过消息驱动的,没有阻塞说明有消息要处理,阻塞是因为没有消息,或消息的延时时间未到
3.Handler 线程切换由于线程间内存共享
为什么会切换线程:一个app里面的内存,就是我们进程,进程与进程之前的内存是不共享的,但是线程与线程之间的内存是共享的,在子线程能拿到主线程创建的handler,而主线程的looper 对象是在ActivityThread中main方法创建的 那looper.looper()也在主线程中,那looper 中的dispatchMessage().也是在主线程 ,也就是创建和接受在主线程中 只有发送和入队是在子线程中
看完handler源码 很明显的看到handler 的设计模式就是一个生产者与消费者的模式
总结:
1.一个线程中只能有一个looper 所以,一个线程也就只能有一个MessageQueue,因为 Looper在构造方法中创建MessageQueue 队列 。
2.主线程的Looper 调用默认在ActivityThread中已经调用开启。
3.Handler是消息驱动的所以Looper轮训器一直轮询消息并没有阻塞主线程。
4.Handler实现线程切换的原因是线程间内存是共享的。
5.MessageQueue中有两个主要的方法,一个是消息入队方法enqueueMessage() 一个是消息出队方法 next()
(1)enqueueMessage():消息入队,插入消息的时候对消息进行排序
(2)next():消息出队,next 方法在looper中调用,消息出队过程中会出现队列阻塞,阻塞原因就是队头的消息延迟时间未到,或者现在消息队列没有消息