当前位置:网站首页>The Handler you really understand?
The Handler you really understand?
2022-08-02 14:51:00 【A leaf can't cover the sky】
目录
一、Handler、Looper、MessageQueue、Message的关系
三、Message distribution analytic
3.3、ThreadLocal(Can be extended content to understand)
先说两句废话,Before this for a long time,由于各种原因吧,Really long time no wrote,Human nature of the inert really terrible,We should try to overcome inertia,好在今天,我又回来了,Pick up learning summary,养成一个良好的学习习惯,Rui hired!
Today we are going to review summarizeHandler的相关知识点,Sort out the problem of some of the common,Can turn out when next time we need to have a look,The document is a reference for class network《移动端架构师》课程中的Handler部分的讲解.
一、Handler、Looper、MessageQueue、Message的关系
1.1、消息机制流程
首先来看下面这张图:A thread of up to aLooper;一个Looper有一个MessageQueue;一个MessageQueue对应多个message;一个MessageQueue对应多个Handler.Through this chart we to the wholeAndroidMessaging process together to understand:
AndroidMessage mechanism is basically by theHandler、Looper、MessageQueue、MessageThis a few classes to support,实际上整个AndroidSystem is a system message driven,If there is no this a few class,Android中的各种事件、All kinds of messages cannot be enforced,So it is very important to understand the message mechanism is.
Handler:Responsible for sending messages and handle the message,No matter the way we use which sends the message,最终都是通过enqueueMessage将消息加入到MessageQueueThe message queue to.
MessageQueue:Because we sent messages is not immediately get perform,So the message need a place to store it,因此就有了MessageQueue这个消息队列.Its interior is implemented by a singly linked list,The same time will only be triggered by a message,So the message in the queue will in itself be triggered time order in the queue,无论何时,This message queue head it triggered time are the most close to.
A queue of a total of three types of news:BarrierMessage:同步屏障消息;AsyncMessage:异步消息;Message:同步消息.
The message in the queue to allow them to perform in the necessary time?此时就需要依靠LooperThe news of the infinite loop drive.
Looper:The news of the infinite loop drive,It is responsible for the associated thread and message distribution,Looper创建的时候会创建一个MessageQueue,Looper会通过loop()Methods open an infinite loop,It will start from scratch traversal queue,不断调用MessageQueue的next()Methods to check whether there is a meet the conditions of news,Have, take out the message will be distributed and perform,No obstruction innext()方法中.当Looper的quit()方法被调用时会调用MessageQueue的quit(),此时next()会返回null,之后loop()Methods will follow.
Message:消息,Within each message are the following several important properties of:
when:This message is executed the timestamp of the,This is it the only on the basis of sorting in the queue;
next:Because the message queue is a singly linked list,So within each message will contain a message under the reference relationship;
target:On behalf of each message is whichHandler对象发送的,Also by thisHandlerObject to spend.
The whole process of the message loop summarized below:
Handler通过sendMessage()方法发送消息Message到消息队列MessageQueue中,Looper通过loop()Methods get the trigger conditionMessage,然后将Message交给对应的Handler,最后Handler调用自身的handleMessage()方法处理Message.
1.2、Looper创建
We usually use in the main threadHandler时,Did not write aboutLooper的相关代码,Why can also send message?
- 在ActivityThread.main()方法中调用了Looper.prepareMainLooper()方法创建了主线程的Looper,然后调用了loop()Open the message loop
- 通常我们说ActivityThread就是主线程,But in fact it is not a thread,而是主线程操作的管理者
class Handler{
public Handler() {this(null, false);}
public Handler(boolean async) {this(null, async);}
public Handler(Callback callback, boolean async) {
// 如果没调用Looper.prepare则为空
// 主线程创建Handler不用创建Looper,就是因为ActivityThreadIn the process of entry callsLooper.prepareMainLooper()帮我们创建好了
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
Looper.myLooper()Is is how to ensure access to this threadlooper对象的呢?
class Looper{
// sThreadLocal是static变量,Can understand simple it is equivalent toMap,key是线程,value是Looper
// You can use the current threadsThreadLocal获取当前线程所属的Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// LooperThe thread's message queue
final MessageQueue mQueue;
private static void prepare(boolean quitAllowed) {
// 一个线程只能有一个Looper,prepare不能重复调用
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
}
public static @Nullable Looper myLooper() {
// 具体看ThreadLocal类源码的get方法
// 简单理解相当于map.get(Thread.currentThread())获取当前线程的Looper
return sThreadLocal.get();
}
}
二、News team resolved
2.1、sendMessage
According to our usual experience,We can know when to send a message to,一般有两种方式:postXXX和sendXXX,No matter we use what kind of way,最后都会以MessageIn the form of inserted into the message queue,比如你使用post发送消息时,都会通过getPostMessage()将Runnable对象包装成Message对象,然后再次调用sendMethod to add it to the queue:
class Handler{
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
// 将Runnable包装成Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
}
class Message{
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
// 消息复用
public static Message obtain() {
// Singly linked list in the form of object pool
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
// 消息回收
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
This is used when get messagesMessage.obtain()方法,这是MessageThe ability to provide us with the news of the reuse pool,Inside it will cache50个Message对象,Use this approach can be directly from the reuse pool message,不用new对象,Will not produce a lot of temp,因此不会造成CPUThe problem of high memory usage rate.Since the message pool's ability to provide a Shared object,So will certainly provide object recovery ability,When each message is carried out after completion of,都会走到recycleUnchecked()方法中,从而将Message对象重置,数据被清空,Then insert it into the head of the linked list node,Thus formed a team head reuse message pool.
2.2、enqueueMessage
MessageQueue是一个单项链表,So it's each node contains anextObject is used to point to the next node:
No matter use which kinds of sending the message the way,最终都会走到enqueueMessage方法里面:
- If the new message is triggered timewhenThan any a message in the queue early,So is inserted into the team head,唤醒Looper
- If the current message queue head is synchronous barrier,New messages are asynchronous message,So also want to wake upLooper
To see this part of the code below,Through the comments to do that:
class Handler{
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 把msg的target赋值成当前的Handler
msg.target = this;
...
return queue.enqueueMessage(msg, uptimeMillis);
}
}
class MessageQueue{
boolean enqueueMessage(Message msg, long when) {
// 普通消息的target字段不能为空,Or don't know whom to handle this message
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.when = when;
// mMessageIs always a team head message
Message p = mMessages;
boolean needWake;
// If the team head message is empty,或消息的when=0不需要延迟,或newMsg.when<headMsg.when,则插入队头
if (p == null || when == 0 || when < p.when) {
// 插入队头,把newMsg.nextPoint to the team head nodep,newMsg赋值给mMessage,newMsgWas the team head
msg.next = p;
mMessages = msg;
// 如果当前Looper处于休眠状态,Is the need to wake up after insert message
needWake = mBlocked;
} else {
// Do you want to wake upLooper=The dormant&Team head messages are synchronous barrier&New messages are asynchronous message
// In order to make an asynchronous message executed as early as possible
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
// Find the message place in chronological order
// msg.when=2-->msg.when=4-->msg.when=6
// msg.when=2-->msg.when=4-->【newMsg.when=5】-->msg.when=6
// prevThe last object is the queue timestamp is less than the new message timestampmessage对象
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// We must adjust the relationship between a reference list node
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 唤醒Looper,走的是native方法
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
}
2.3、postSyncBarrier同步屏障消息
- message.target=null,This kind of news is not true to perform,它起到flag标记的作用,MessageQueueAt the time of the message queue traversal,If the team head is synchronous barrier message,So will ignore the synchronous message,Priority for implementation of asynchronous message.这就是它的目的,In general asynchronous messages and synchronization barrier will be used together.
- 异步消息&同步屏障使用场景
- ViewRootImplVertical sync signal receiving screen event is used to driveUI测绘
- ActivityThread接收AMSThe event driven life cycle
- InputMethodManagerDistribution of soft keyboard input events
- PhoneWindowManagerDistribution of phone page events
class MessageQueue{
public int postSyncBarrier() {
// currentTimeMillis()系统当前时间,即日期时间,The system can modify Settings,如果设置系统时间,Change the time value will be
// uptimeMillis()Since after the boot time,Do not include deep sleep time
// sendMessageDelay,postDelayAlso use the timestamp
// Mean to send this message,During this period if the device into a dormant state,Then the message will not be performed,After wake up will be device to perform
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
// 从消息池复用,Build the new message body
final Message msg = Message.obtain();
// 并没有给target字段赋值,To distinguish whether the synchronization barrier,就看target是否等于null
msg.when = when;
Message prev = null;
Message p = mMessages;
// Iterates through all message queue,直至找到一个message.when>msg.when的消息,The location of the decided to insert a new news
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// If find the right position is inserted into the
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
// 如果没有找到,Directly on the team head
msg.next = p;
mMessages = msg;
}
return token;
}
}
}
注意:Barrier message when inserted into the queue,Not to take the initiative to awaken the thread,Because of the barrier messages don't need to be performed.When news is so barrier is removed from the queue?答案是:Who add who will remove,比如ViewRootImplAt the time of receiving vertical synchronous signal sends an asynchronous message and a synchronous barrier message,When is in charge of the implementation of asynchronous message,ViewRootImplWill the synchronous barriers removed from the queue.
三、Message distribution analytic
3.1、Looper.loop()
The queue messages are able to get out,According to the analysis of the flow chart,它是靠Looper类调用loop()Methods open an infiniteforCirculation message drive,如下代码所示:
class Looper{
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
......
for (;;) {
// 无限循环,调用MessageQueue的nextAccess to the executable message,Note that the official comments,这里可能会阻塞
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
......
// 分发消息
msg.target.dispatchMessage(msg);
......
// 回收message对象,Keep the reuse
msg.recycleUnchecked();
}
}
}
在loop()方法的内部,会调用MessageQueue的next()Method to get an executable message object.
注意:在Message msg = queue.next();This line of code behind the official gave a comment:might block,它的意思是,该next()Methods into a state may make the current thread blocking,This method will not have a return value at this time,The subsequent code won't be executed,So the infinite hereforCirculation is not always empty polling like this,Its purpose is to keep this thread to exit.
那么接下来我们就来看一下MessageQueue的next()方法:
class MessageQueue{
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
// 如果nextPollTimeoutMillis>0,则Looper会进入休眠状态,The first value is equal to the0,所以不会
// If the first cycle was not found to deal with the news of the,则nextPollTimeoutMillis会被更新
// 在第二次循环时,如果nextPollTimeoutMillis!=0,Looper线程会进入阻塞状态
// During this period the main thread has no real work to do,会释放CPU资源,This method will timeout autonomous recovery,Passive recovery or insert a new news
nativePollOnce(ptr, nextPollTimeoutMillis);
// 当Looper被唤醒时,会继续向下执行
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// If the team head's barrier,Are trying to find an asynchronous message
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// If above the time that the news of the find is not to need to perform
// 则更新nextPollTimeoutMillis,即:The next cycle need blocking time value
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Found the message of the need to deal with
mBlocked = false;
// As a result of this message is about to be,So we need to remove it from the queue
// By adjusting the node points to the relationship between,To achieve the purpose of queue element removed
if (prevMsg != null) {
// To delete messages in the queue in the middle of the position
prevMsg.next = msg.next;
} else {
// Want to delete the messages in the team head position
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 如果没有消息,即队列为空,LooperWill enter a state of infinite block,Until a new message arrives at
nextPollTimeoutMillis = -1;
}
// When the queue messages for empty,Is all the tasks are done,Or team head message has reached the execution time point
// Distributed notice at this time LooperIs about to enter idle
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0)return
}
// 注册MessageQueue.IdleHandler,Can monitor the current threadLooper是否处于空闲状态,Also means that if thread is idle
// In the main thread can listen for this event to do lazy initialization,数据加载,Optimize operation such as log report
// Is not a task to submit,Avoid taking important resources
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
boolean keep = false;
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 此时置为0,Since listening to the threads of free,那么在queueIdle回调里,May will produce a new message again
// In order to make implementation of news as early as possible,So I don't need to sleep at this time
nextPollTimeoutMillis = 0;
}
}
}
3.2、Handler.dispatchMessage
Here we need to know is the priority message distribution:
1. Message的回调方法:message.callback.run(),优先级最高
2. Handler的回调方法:Handler.mCallback.handleMessage(msg)
3. Handler的默认方法:Handler.handleMessage(msg)
class Handler{
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
}
For the use of these three ways,We simply illustrated as follows:
// 1.直接在Runnable中处理任务
Handler handler1 = new Handler();
// When the news in the consumer is the callback toMessage的Callback,这个callbackIs passed in hereRunnable对象
handler1.post(new Runnable() {
@Override
public void run() {
}
});
// 2.使用Handler.CallbackTo receive the message processing
Handler handler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
return true;
}
});
// 3.使用handleMessageTo receive the message processing
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
3.3、ThreadLocal(Can be extended content to understand)
ThreadLocal提供了线程独有的局部变量存储能力,可以在整个线程存活的过程中随时取用.
Thread--->ThreadLocalMap--->Entry--->key:ThreadLocal & value:T
ThreadLocalMap的数据模型:
- 每个线程对应一个ThreadLocalMap对象,Nature is a hash array implementationmap
- 每个元素都是Entry对象,key=threadLocal,value为任意值
class ThreadLocal{
public void set(T value) {
// 获取当前线程的ThreadLocalMap对象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); // 更新值,key=threadLocal
else
createMap(t, value); // 创建并设置值
}
void createMap(Thread t, T firstValue) {
// 每个线程都有一个ThreadLocalMap,key为threadLocal对象
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
public T get() {
// 获取当前线程对象,通过线程获取ThreadLocalMap对象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 不为空则取值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
// Get the calledthreadLocal的初始值
T value = initialValue();
// 得到当前线程
Thread t = Thread.currentThread();
// To obtain or create each threadThreadLocalMap,And the initial values stored in
// So as to realize variable copy,Become a thread exclusive
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
static class ThreadLocalMap{
// 本质是一个数组
private Entry[] table;
// 继承自WeakReference
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
// keyIf be refer to hold,As if the object held by reference to the strong reference is empty,在GCScan will then be recycled
// Thread pool must be noticed when using after completion of task execution,需手动把Value移除掉,Otherwise easy to cause a memory leak
super(k);
// value Strong reference value assignment
value = v;
}
}
}
}
四、Handler高频面试考点
1、为什么主线程不会因为Looper.loop()里的死循环卡死?
主线程确实是通过Looper.loop()进入了循环状态,Because the main Cheng Cai won't like usually create common thread,当可执行代码执行完后,线程生命周期就终止了.
在主线程的MessageQueue没有消息时,便阻塞在MessageQueue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,Until a new message arrives at,So the main thread is dormant in most of the time,并不会消耗大量CPU资源.
这里采用的Linux的epoll机制,是一种IO多路复用机制,可以同时监控多个文件描述符,当某个文件描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作拿到最新的消息,进而唤醒等待的线程.
2、post和sendMessageThis what is the difference between two ways of sending messages?
post一类的方法发送的是Runnable对象,But in the final will be encapsulated intoMessage对象,将Runnable对象赋值给Message对象中的callback变量,然后交由sendMessageAtTime()方法发送出去.在处理消息时,会在dispatchMessage()方法里首先被handleCallback(msg)方法执行,实际上就是执行Message对象里面的Runnable对象的run()方法.
sendMessage一类的方法发送的直接是Message对象,处理消息时,在dispatchMessage里优先级会低于handleCallback(msg)方法,是通过自己重写的handleMessage(msg)方法执行.
3、为什么要通过Message.obtain()方法获取Message对象?
Message.obtain()方法可以从全局消息池中得到一个空的Message对象,这样可以有效节省系统资源.同时,通过各种obtain重载方法还可以得到一些Message的拷贝,或对Message对象进行一些初始化.
4、Handler实现发送延迟消息的原理是什么?
我们平时用postDelayed()与sendMessageDelayed()来发送延迟消息,其实最终都是将延迟时间转为确定时间,然后通过sendMessageAtTime()--->enqueueMessage--->queue.enqueueMessage这一系列方法将消息插入到MessageQueue中.所以并不是先延迟再发送消息,而是直接发送消息,再借助MessageQueue的设计来实现消息的延迟处理.
消息延迟处理的原理涉及MessageQueue的两个静态方法MessageQueue.next()和MessageQueue.enqueueMessage().通过Native方法阻塞线程一定时间,As news of the execution time to remove again after messages.
5、同步屏障SyncBarrier是什么?有什么作用?
在一般情况下,同步和异步消息处理起来没有什么不同,只有在设置了同步屏障后才会有差异.同步屏障从代码层面上看是一个Message对象,但是其target属性为null,用以区分普通消息.在MessageQueue.next()Methods if the current message is a synchronous barrier,则跳过后面所有的同步消息,找到第一个异步消息来处理.
6、IdleHandler是什么?有什么作用?
当消息队列没有消息时调用或者如果队列中仍有待处理的消息,但都未到执行时间时,也会调用此方法,Used to monitor the main thread of the idle state.
7、Why not staticHandler会导致内存泄漏?如何解决?
非静态的内部类、匿名内部类、局部内部类都Implicitly hold其外部类的引用,举个栗子:在Activity中创建的HandlerImplicitly holdActivity的引用.
当我们在主线程使用Handler的时候,Handler会默认绑定这个线程的Looper对象,并关联其MessageQueue,Handler发出的所有消息都会加入到这个MessageQueue中.Looper对象的生命周期贯穿整个主线程的生命周期,所以当Looper对象中的MessageQueueThere is also not processedMessage时,因为每个Message都持有Handler的引用,所以Handler无法被回收,It holds a reference external classActivity也就无法被回收,造成内存泄漏.
解决方案:使用静态内部类+弱引用的方式
class MainActivity{
private static class MyHandler extends Handler {
//弱引用,在垃圾回收时,activity可被回收
private WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
this.mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//清空handler管道和队列
mHandler.removeCallbacksAndMessages(null);
}
}
8、How to pop up in the child threadToast?
调用Looper.prepare()以及Looper.loop(),But bear in mind that after task execution thread,需要手动调用Looper.quitSafely(),否则线程不会结束.
OK,Today's content is about to end here,谢谢大家,下期再会!
边栏推荐
- VS Code无法安装插件之Unable to install because, the extension '' compatible with current version
- binlog与iptables防nmap扫描
- C语言待解决
- spark(standalone,yarn)
- Kubernetes架构和组件
- 宝塔搭建PHP自适应懒人网址导航源码实测
- Flask contexts, blueprints and Flask-RESTful
- 【c】大学生在校学习c语言常见代码
- drf source code analysis and global catch exception
- PHP版本切换:5.x到7.3
猜你喜欢
随机推荐
C语言初级—用一角,两角,五角和一元组成3.5元有多少种组合方法
华为路由交换
主存储器(一)
uniapp小程序禁止遮罩弹窗下的页面滚动的完美解决办法
绕过正则实现SQL注入
Flink时间和窗口
Flask contexts, blueprints and Flask-RESTful
MySQL知识总结 (六) MySQL调优
Redis持久化机制
liunx下mysql遇到的简单问题
关于UDF
drf view component
C语言sizeof和strlen的区别
C语言日记 4 变量
spark写sql的方式
CTF-XSS
数据乱码问题—更改mysql字符编码
spark on yarn
Flask request application context source code analysis
St. Regis Takeaway Notes - Lecture 05 Getting Started with Redis