为什么会呈现内存透露问题呢?

  • 剖析

    • Handler应用是用来进行线程间通信的,所以新开启的线程是会持有Handler援用的,如果在Activity等中创立Handler,并且是非动态外部类的模式,就有可能造成内存透露
    • 非动态外部类是会隐式持有外部类的援用,所以当其余线程持有了该Handler,线程没有被销毁,则意味着Activity会始终被Handler持有援用而无奈导致回收
    • MessageQueue中如果存在未解决完的Message,Message的target也是对Activity等的持有援用,也会造成内存透露
  • 解决的方法

    • 应用动态外部类 + 弱援用的形式

      • 动态外部类不会持有外部类的的援用,当须要援用外部类相干操作时,能够通过弱援用还获取到外部类相干操作,弱援用是不会造成对象该回收回收不掉的问题,不分明的能够查阅JAVA的几种援用形式的具体阐明
    • 在外部类对象被销毁时,将MessageQueue中的音讯清空

在应用Handler时,通常是通过Handler.obtainMessage()来获取Message对象的,而其外部调用的是Message.obtain()办法,那么问题来了,为什么不间接new一个Message,而是通过Message的静态方法obtain()来失去的呢?

  • 应用obtain获取Message对象是因为Message外部保护了一个数据缓存池,回收的Message不会被立马销毁,而是放入了缓存池,在获取Message时会先从缓存池中去获取,缓存池为null才会去创立新的Message

Message的存储模式是什么

  • Message的存储是链表的模式,next相当于链表的尾指针

新的首部如果阻塞了,须要唤醒线程。为什么会有线程的阻塞呢?

  • 其实MessageQueue外部的音讯是按须要发送的工夫点从小到大排列的,前面会剖析到,从以后if里的when判断也能看出一二,当队首的Message未达到发送的工夫点时,阐明其以后所有的音讯都未达到发送的工夫,下面说过,Handler发送音讯并不是通过定时器发送的。当队首Message(最近须要发送的Message)未达到发送工夫点时,线程被阻塞,所以这里须要依据线程是否阻塞看是否须要唤醒线程,这样能力使新退出的Message能及时发送进来,不会被阻塞

一个线程能够有几个Looper、几个MessageQueue和几个Handler?

  • 在 Android 中,Looper类利用了ThreadLocal的个性,保障了每个线程只存在一个Looper对象。Looper构造函数中创立了MessageQueue对象,因而一个线程只有一个MessageQueue。能够有多个Handler

能够在子线程间接创立一个Handler吗?会呈现什么问题,那该怎么做?

  • 不能在子线程间接new一个Handler。因为Handler的工作依赖于Looper,而Looper又是属于某一个线程的,其余线程不能拜访,所以在线程中应用Handler时必须要保障以后线程中Looper对象并且启动循环。不然会抛出异样

Looper死循环为什么不会导致利用卡死,会耗费大量资源吗?

  • 对于线程即是一段可执行的代码,当可执行代码执行实现后,线程生命周期便该终止了,线程退出。而对于主线程,咱们是绝不心愿会被运行一段时间,本人就退出,那么如何保障能始终存活呢?简略做法就是可执行代码是能始终执行上来的,死循环便能保障不会被退出,例如,binder线程也是采纳死循环的办法,通过循环形式不同与Binder驱动进行读写操作,当然并非简略地死循环,无音讯时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去解决其余事务呢?通过创立新线程的形式。真正会卡死主线程的操作是在回调办法onCreate/onStart/onResume等操作工夫过长,会导致掉帧,甚至产生ANR,looper.loop自身不会导致利用卡死

-主线程的死循环始终运行是不是特地耗费CPU资源呢? 其实不然,这里就波及到Linux pipe/epoll机制,简略说就是在主线程的MessageQueue没有音讯时,便阻塞在Loop的queue.next()中的nativePollOnce()办法里,此时主线程会开释CPU资源进入休眠状态,直到下个音讯达到或者有事务产生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采纳的epoll机制,是一种IO多路复用机制,能够同时监控多个描述符,当某个描述符就绪(读或写就绪),则立即告诉相应程序进行读或写操作,实质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会耗费大量CPU资源

Handler原理

Handler创立音讯

  • 每一个音讯都须要被指定的Handler解决,通过Handler创立音讯便能够实现此性能。Android音讯机制中引入了音讯池。Handler创立音讯时首先查问音讯池中是否有音讯存在,如果有间接从音讯池中获得,如果没有则从新初始化一个音讯实例。应用音讯池的益处是:音讯不被应用时,并不作为垃圾回收,而是放入音讯池,可供下次Handler创立音讯时应用。音讯池进步了音讯对象的复用,缩小零碎垃圾回收的次数。
  • 音讯的创立流程如图所示

Handler发送音讯

  • UI主线程初始化第一个Handler时会通过ThreadLocal创立一个Looper,该Looper与UI主线程一一对应。应用ThreadLocal的目标是保障每一个线程只创立惟一一个Looper。之后其余Handler初始化的时候间接获取第一个Handler创立的Looper。Looper初始化的时候会创立一个音讯队列MessageQueue。至此,主线程、音讯循环、音讯队列之间的关系是1:1:1
  • Hander持有对UI主线程音讯队列MessageQueue和音讯循环Looper的援用,子线程能够通过Handler将音讯发送到UI线程的音讯队列MessageQueue中
  • Handler、Looper、MessageQueue的初始化流程如图所示

Handler解决音讯

  • UI主线程通过Looper循环查问音讯队列UI_MQ,当发现有音讯存在时会将音讯从音讯队列中取出。首先剖析音讯,通过音讯的参数判断该音讯对应的Handler,而后将音讯散发到指定的Handler进行解决
  • 子线程通过Handler、Looper与UI主线程通信的流程如图所示

编程这个行业是要一直学习,一直冲破的。技术在不断更新,你不致力,眨眼就会被他人甩开几条街,只有一直的学习提高,能力不被时代淘汰。关注我,每天分享常识干货!