线程阻塞
涉及到了操作系统的一些内容(需要了解的可以来这里)
简单来说,Android是基于Linux系统的,而在Linux系统中,所有的进程都是从init进程直接或者间接fork出来的,App也不例外。每开启一个App,就相当于开启一个新的线程。在21世纪的进程,本着物尽其用的原则,就必然存在异步处理,也就需要了多个线程。多个线程之间就会存在调度的情形,而线程阻塞就是其中的一种状态(Android的多线程这个博客讲的挺详细)。
postDelayed干了什么
- Handler.postDelayed最后通过MessageQueue.enqueueMessage方法将Message插入MessageQueue中,执行插入操作唤醒当前线程,由于MessageQueue会有多个线程操作,这里用了同步锁保证线程操作安全。所以Handler.postDelayed不会造成线程阻塞,只是执行了Message入队的操作。
- 对于阻塞的说法也是有的,Handler对Message的处理是管道操作,MessageQueue对所有Message统一进行了管理,在Looper.loop()不停对MessageQueue遍历的时候会进行一个判断,如果队列头部的Message没到执行时间,计算一下剩余执行时间,然后调用nativePollOnce()阻塞线程,直到阻塞时间到或者下一次有Message进队。
Handler.java
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// mQueue是Handler绑定的Lopper中的Message队列
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用了MessageQueue的插入Meessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
...
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//插入Message
msg.next = p; // invariant: p == prev.next
prev.next = msg;
...
if (needWake) {
//唤醒线程
nativeWake(mPtr);
}
}
return true;
}
对内存的影响
Handler导致内存泄漏一般发生在发送延迟消息的时候,当Activity关闭之后,延迟消息还没发出,那么主线程中的MessageQueue就会持有这个消息的引用,而这个消息是持有Handler的引用,而handler作为匿名内部类持有了Activity的引用,所以就有了以下的一条引用链。
主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity
其根本原因是因为这条引用链的头头,也就是主线程,是不会被回收的,所以导致Activity无法被回收,出现内存泄漏,其中Handler只能算是导火索。
而我们平时用到的子线程通过Handler更新UI,其原因是因为运行中的子线程不会被回收,而子线程持有了Actiivty的引用(不然也无法调用Activity的Handler),所以就导致内存泄漏了,但是这个情况的主要原因还是在于子线程本身。
参考链接:https://cloud.tencent.com/developer/article/1800399