看这篇文章的时候需要有以下疑惑
- 为什么子线程不能够直接创建Handler而主线程可以直接new Handler()?
- Handler在平时使用的时候我们应该注意哪些方面?
- 为什么一个线程只能有一个Looper?
- 两个子线程如何进行Handler操作?
1.Handler 工作流程浅析
异步通信准备 => 消息入队 => 消息循环 => 消息处理
-
异步通信准备
假定是在主线程创建 Handler,则会直接在主线程中创建处理器对象Looper
、消息队列对象MessageQueue
和 Handler 对象。需要注意的是,Looper
和MessageQueue
均是属于其 创建线程 的。Looper
对象的创建一般通过Looper.prepareMainLooper()
和Looper.prepare()
两个方法,而创建Looper
对象的同时,将会自动创建MessageQueue
,创建好MessageQueue
后,Looper
将自动进入消息循环。此时,Handler
自动绑定了主线程的Looper
和MessageQueue
。 -
消息入队
工作线程通过Handler
发送消息Message
到消息队列MessageQueue
中,消息内容一般是 UI 操作。发送消息一般都是通过Handler.sendMessage(Message msg)
和Handler.post(Runnabe r)
两个方法来进行的。而入队一般是通过MessageQueue.enqueueeMessage(Message msg,long when)
来处理。 -
消息循环
主要分为「消息出队」和「消息分发」两个步骤,Looper
会通过循环 取出 消息队列MessageQueue
里面的消息Message
,并 分发 到创建该消息的处理者Handler
。如果消息循环过程中,消息队列MessageQueue
为空队列的话,则线程阻塞。 -
消息处理
Handler
接收到Looper
发来的消息,开始进行处理。
2. 问题1:Looper为什么不能被创建两次?
可以看见调用Prepare的时候就是在创建Looper,创建前会在前会在ThreadLocal中去获取一次Loop如果有者直接异常,没有的话创建一个唯一的Looper并且把他存储到ThreadLocal中。
关于创建还有个prepareMainLooper方法,这个主要是在Activity主线程中运用,而在构建Activity的时候系统会自动创建Looper,所以这就是为什么我们可以在主线程中直接new Handler()而不报错了。
接下来我们来看Looper的构造方法,没错在创建Looper的时候系统也默认创建了MessageQueue同时也把当前线程进行了存储。
A.接下来我们看Loop
第2行:
方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。先拿到该looper实例中的mQueue(消息队列)
然后进入就进入了我们所说的无限循环。
然后取出一条消息,如果没有消息则阻塞。
接下来使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象.
最后释放消息占据的资源。
接下来看看dispatchMesaage(Message msg)做了什么
Looper主要作用:
1.在主线程中 Looper
对象自动生成,无需手动生成。而在子线程中,一定要调用 Looper.prepare()
创建 Looper
对象。如果在子线程不手动创建,则无法生成 Handler
对象。
2. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
3. loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。好了,我们的异步消息处理线程已经有了消息队列(MessageQueue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,
4.分发消息给 Handler
的过程为:根据出队消息的归属者,通过 dispatchMessage(msg)
进行分发,最终回调复写的 handleMessage(Message msg)
。
5.在消息分发 dispatchMessage(msg)
方法中,会进行 1 次发送方式判断:
1. 若 msg.callback
属性不为空,则代表使用了 post(Runnable r)
发送消息,则直接回调 Runnable
对象里面复写的 run()
。
2. 若 msg.callback
属性为空,则代表使用了 sendMessage(Message msg)
发送消息,直接回调复写的 handleMessage(msg)
。
Handler的构造函数
可以看见通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在又获取了这个Looper实例中保存的MessageQueue(消息队列),这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
Hadnler的另外一种使用方式
从官方注释可以看到,这会直接将 Runnable
对象加到消息队列中,我们来看看 `getPostMessage(r) 到底做了什么。
我们上面的分析是对的。在 getPostMessage(Runnable r)
方法中,我们除了通过 Message.obtain()
方法来创建消息对象外,专门把 Runnable
对象赋值给了 callback
,这样才用了上面做消息分发的时候,通过这个标记来判断是用的 post()
还是 sendMessage()
方式。
到底是如何发消息的?
一直在说通过 sendMessage()
方式来发消息,到底这个消息是怎么发送的呢?
直接看 sendMessageAtTime()
。
enqueueMessage()
里面做了什么?
其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。
new Thread()
{
private Handler handler;
public void run()
{
Looper.prepare();
handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{Log.e("TAG",Thread.currentThread().getName());};
};
//必须调用Loop循环接受消息 //由于Loop是会阻塞进程所以后面的代码不会执行
Looper.loop();
}
一定要记得关闭Handler
fun closeHandler() { HanlderHeard.handler?.looper?.quit(); HanlderHeard.handler?.removeCallbacksAndMessages(null); HanlderHeard.handler = null; }
参考:https://blog.youkuaiyun.com/lmj623565791/article/details/38377229