Handler机制

本文深入解析Handler的工作流程,包括消息入队、消息循环与消息处理。重点探讨为什么子线程不能直接创建Handler,一个线程为何只能有一个Looper,以及如何在不同线程间使用Handler进行通信。同时,文章指出Looper的无限循环过程以及Handler如何分发消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看这篇文章的时候需要有以下疑惑

  • 为什么子线程不能够直接创建Handler而主线程可以直接new Handler()?
  • Handler在平时使用的时候我们应该注意哪些方面?
  • 为什么一个线程只能有一个Looper?
  • 两个子线程如何进行Handler操作?

1.Handler 工作流程浅析

异步通信准备 => 消息入队 => 消息循环 => 消息处理

  • 异步通信准备
    假定是在主线程创建 Handler,则会直接在主线程中创建处理器对象 Looper、消息队列对象 MessageQueue 和 Handler 对象。需要注意的是,LooperMessageQueue 均是属于其 创建线程 的。Looper 对象的创建一般通过 Looper.prepareMainLooper()Looper.prepare() 两个方法,而创建 Looper 对象的同时,将会自动创建 MessageQueue,创建好 MessageQueue 后,Looper 将自动进入消息循环。此时,Handler 自动绑定了主线程的 LooperMessageQueue

  • 消息入队
    工作线程通过 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 

https://www.jianshu.com/p/7340c3d4cec6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值