Android基础——Handler消息传递源码解析

Handler源码解析

采用Android源码,理解四大成员建立关系的过程
Android源码链接

1.创建Handler

·Handler的构造函数

在这里插入图片描述
·Callback(用来处理Message的一种手段,如果没有参数,就重写Handler的handleMessage方法)
在这里插入图片描述
·in order to使Handler能够处理Message(都要实现handlerMessage方法):
1)向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法
2)无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法
·向消息队列中发送消息(sendMessage…)
调用顺序(箭头表示调用方向):
请添加图片描述
sendMessage的post(调用了getPostMessage方法)
请添加图片描述
getPostMessage方法(传入了一个Runnable对象,得到一个Message对象)
在这里插入图片描述
在getPostMessage方法中,创建了一个Message对象,并将传入的Runnable对象赋值给Message的callback成员字段,然后返回该Message,然后在post方法中该携带有Runnable信息的Message传入到sendMessageDelayed方法中。
也就是所有的postXXX方法内部都需要借助sendMessageXXX方法来实现,postXXX依赖sendMessageXXX,所以postXXX方法可以通过sendMessageXXX方法向消息队列中传入消息,只不过通过postXXX方法向消息队列中传入的消息都携带有Runnable对象(Message.callback)。
·post…和sendMessage…方法的关系图
请添加图片描述
·可以看到在Handler中所有可以直接或间接向消息队列发送Message的方法最终都调用了sendMessageAtTime方法
请添加图片描述
enqueueMessage方法:
请添加图片描述
====>完整的方法调用顺序
在这里插入图片描述

2.创建Looper

(使用Looper.prepare()方法)
·Looper的构造函数
在这里插入图片描述
·Looper是用来使线程中的消息循环起来的。默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列MessageQueue的。为了能够让线程能够绑定一个消息队列,我们需要借助于Looper:首先我们要调用Looper的prepare方法,然后调用Looper的Loop方法。
在这里插入图片描述
线程Thread和Looper是一对一绑定的,也就是一个线程中最多只有一个Looper对象,<===>Looper的构造函数是private的,
只能通过Looper.myLooper()这个静态方法获取当前线程所绑定的Looper。
·保存对当前线程的引用
在这里插入图片描述
在Looper对象中通过sThreadLocal就可以找到其绑定的线程。ThreadLocal中有个set方法和get方法,可以通过set方法向ThreadLocal中存入一个对象,然后可以通过get方法取出存入的对象。
ThreadLocal在new的时候使用了泛型(泛型类型是Looper),也就是通过ThreadLocal的set和get方法只能写入和读取Looper对象类型,如果调用其ThreadLocal的set方法传入一个Looper,将该Looper绑定给了该线程,相应的get就能获得该线程所绑定的Looper对象。
·Looper.prepare()方法(让Looper做好准备)
在这里插入图片描述
首先通过sThreadLocal.get()拿到线程sThreadLocal所绑定的Looper对象,由于初始情况下sThreadLocal并没有绑定Looper,所以第一次调用prepare方法时,sThreadLocal.get()返回null,不会抛出异常。
------>完成sThreadLocal与Looper的双向绑定:
a. 在Looper内通过sThreadLocal可以获取Looper所绑定的线程;
b.线程sThreadLocal通过sThreadLocal.get()方法可以获取该线程所绑定的Looper对象。
·在执行完了Looper.prepare()之后,我们就可以在外部通过调用Looper.myLooper()获取当前线程绑定的Looper对象。
在这里插入图片描述
在一个线程中,只能调用一次Looper.prepare(),因为在第一次调用了Looper.prepare()之后,当前线程就已经绑定了Looper,在该线程内第二次调用Looper.prepare()方法的时候,sThreadLocal.get()会返回第一次调用prepare的时候绑定的Looper,不是null,这样就会走的下面的代码throw new RuntimeException(“Only one Looper may be created per thread”),从而抛出异常,因此一个线程只能绑定一个Looper对象。
====>在调用Looper.prepare()方法之后,当前线程和Looper的双向绑定完成
·调用**Looper.loop()**方法,使消息队列循环(在该Looper所绑定的线程中执行)

3.创建MessageQueue并绑定当前线程

·消息队列:负责管理顶级程序对象(Activity、BroadcastReceiver等)
·enqueueMessage方法(用于将一个Message放入到消息队列中)
·next方法(从消息队列中阻塞式取出一个Message)
·消息在加入到队列时,会根据 delay 的时间,插入到队列中合适的位置,保证队列的顺序是按照延迟时间从小到达进行排序。方便在后续获取消息的时候,直接获取到队头消息。

4.创建Message(

一个Message对象就是线程需要处理的一个事情)

5.Handler发送和处理消息

问题汇总

1.handler发送的消息是如何到达handlerMessage回调的
在handler源码中可以看到,sendMessage和post方法都是一直往下调用,都调用到了sendMessageAtTime方法,sendMessageAtTime方法又调用了enqueueMessage方法,最后是MessageQueue.enqueueMessage将消息放入到消息队列中;
在MessageQueue的源码中可以看到其中的next方法,该方法返回的是刚刚保存好的message;
在Looper源码中可以看到,在构造函数中实例化了MessageQueue这个类,在loop的for循环中调用了queue.next方法,如果找到了Message就会执行msg.target;
在Message类中可以看到,这个target其实就是Handler对象,就是调用Handler的
handlerMessage方法。
2.Looper是在哪里被调用的?
主线程的Looper其实在app初始化的时候就已经初始化并且开始Loop无限循环获取Message了,具体代码在ActivityThread的main方法中,其中prepareMainLooper就是初始化Looper,然后还调用了loop方法。
3.如何保证Looper的唯一性(一个线程不会出现多个Looper)?
在Looper的prepare中,这里是对Looper的初始化
在这里插入图片描述
可以看到,Looper的设置并不是简单的set和get,而是通过ThreadLocal类来操作的,在ThreadLocal类中,Looper的设置和获取都是需要传入线程参数的,Looper唯一也就意味着MessageQueue也是唯一的,因为MessageQueue是在Looper构造方法里初始化一次的,但是Handler可以有多个,因为我们每初始化一个Handler对象,都会被Message记录,最后调用相应Handler的handleMessage方法,并不会有冲突。
4.消息是怎么实现延时的?
那个for死循环中实现的,对每个message的延时时间进行对比,如果当前消息的延时时间短,则更改指针指向,将当前消息插入到队列中,如果发送消息的时间没到,则不交给looper:Looper在获取消息是会对时间进行判断,如果时间没到就不返回message,就相当于本息循环没有到达时间需要发送的消息。
5.生产者—消费者模式
handler把消息放到消息队列中,looper到消息队列中去取,一个生产一个消费。

猜你喜欢

转载自blog.csdn.net/weixin_44901971/article/details/127560549
今日推荐