Handler消息机制,postDelayed会造成线程阻塞吗?对内存有什么影响?

线程阻塞

涉及到了操作系统的一些内容(需要了解的可以来这里

简单来说,Android是基于Linux系统的,而在Linux系统中,所有的进程都是从init进程直接或者间接fork出来的,App也不例外。每开启一个App,就相当于开启一个新的线程。在21世纪的进程,本着物尽其用的原则,就必然存在异步处理,也就需要了多个线程。多个线程之间就会存在调度的情形,而线程阻塞就是其中的一种状态(Android的多线程这个博客讲的挺详细)。

postDelayed干了什么

Android消息处理机制

  • 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值