内存泄露在Android开发中非常常见
内存泄露的定义:本该被回收的对象不能被回收而停留在堆内存中
内存泄露出现的原因:当一个对象已经不再被使用时,本该被回收但却因为有另外一个正在使用的对象持有它的引用从而导致它不能被回收。这就导致了内存泄漏。
本文将详细讲解内存泄露的其中一种情况:在Handlr中发生的内存泄露
阅读本文前建议先阅读Android开发:Handlr异步通信机制全面解析(包含Loopr、MssagQuu)
目录目录
.背景我们先来看下日常Handlr的一般用法:
publicclassMainActivityxtndsAppCompatActivity{
OvrridprotctdvoidonCrat(BundlsavdInstancStat){//主线程创建时便自动创建Loopr和对应的MssagQuu,之前执行Loop()进入消息循环supr.onCrat(savdInstancStat);stContntViw(R.layout.activity_main);//实例化Handlr//这里并无指定Loopr,即自动绑定当前线程(主线程)的Loopr和MssagQuuprivatHandlrshowhandlr=nwHandlr(){//通过复写handlrMssag()从而决定如何进行更新UI操作OvrridpublicvoidhandlMssag(Mssagmsg){//UI更新操作}};//启动子线程nwThrad(){Ovrridpublicvoidrun(){supr.run();try{Thrad.slp();}catch(IntrruptdExcption){.printStackTrac();showhandlr.sndEmptyMssagDlayd(0x,);}}}.start();finish();}在上面的例子中,你会发现出现了严重的警告:
Past_Imag.png
从上图可以看出来,这个警告的原因是:该Handlr造成了严重的内存泄漏
那么,该Handlr是怎么样造成内存泄露的呢?
2.内存泄露原因2.造成内存泄露的源头根据图片可以分析,内存泄露显示出现在:
Handlr类
即Handlr四件套:Loopr+MssagQuu+Mssag+Handlr
最终的泄露发生在Handlr类的外部类-MainActivity类
2.2如何造成内存泄露首先,我们需要了解到:
主线程的Loopr对象会伴随该应用程序的整个生命周期
在Java里,非静态内部类和匿名类都会潜在引用它们所属的外部类
在了解到上述两条后,从上面的代码中可以知道:
在发送的延迟空消息(EmptyMssagDlayd)后、消息处理被前,该消息会一直保存在主线程的消息队列里持续0s
在这延时0s内,该消息内部持有对handlr的引用,由于handlr属于非静态内部类,所以又持有对其外部类(即MainActivity实例)的潜在引用,引用关系如下图
引用关系
这条引用关系会一直保持直到消息得到处理,从而,这阻止了MainActivity被垃圾回收器(GC)回收,同时造成应用程序的内存泄漏,如下图:
泄露分析
3.解决方案3.解决方案:使用静态内部类+弱引用上面提到,在Java里,非静态内部类和匿名类都会潜在的引用它们所属的外部类。但是,静态内部类不会。
所以,避免内存泄露的解决方案是:只需要将Handlr的子类设置成静态内部类
同时,还可以加上使用WakRfrnc弱引用持有Activity实例
原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
解决代码如下:
publicclassMainActivityxtndsAppCompatActivity{//将Handlr改成静态内部类privatstaticclassFHandlrxtndsHandlr{//定义弱引用实例privatWakRfrncActivityrfrnc;//在构造方法中传入需要持有的Activity实例publicMyHandlr(Activityactivity){rfrnc=nwWakRfrncActivity(activity);}//通过复写handlrMssag()从而决定如何进行更新UI操作
OvrridpublicvoidhandlMssag(Mssagmsg){//省略代码}}OvrridprotctdvoidonCrat(BundlsavdInstancStat){//主线程创建时便自动创建Loopr和对应的MssagQuu,之前执行Loop()进入消息循环supr.onCrat(savdInstancStat);stContntViw(R.layout.activity_main);//实例化Handlr的子类//这里并无指定Loopr,即自动绑定当前线程(主线程)的Loopr和MssagQuuprivatfinalHandlrshowhandlr=nwFHandlr();//启动子线程nwThrad(){Ovrridpublicvoidrun(){supr.run();try{Thrad.slp();}catch(IntrruptdExcption){.printStackTrac();showhandlr.sndEmptyMssagDlayd(0x,);}}}.start();}3.2解决方案2:当外部类结束生命周期时清空消息队列从上面分析,内存泄露的原因是:当Activity结束生命周期时,Handlr里的Mssag可能还没处理完,从而导致一系列的引用关系。
其实,我们只要在当Activity结束生命周期时清除掉消息队列(MssagQuu)里的所有Mssag,那么这一系列引用关系就不会存在,就能防止内存泄露。
解决方案:当Activity结束生命周期时(调用onDstroy()方法),同时清除消息队列里的所有回调消息(调用rmovCallbacksAndMssags(null))
代码如下:
OvrridprotctdvoidonDstroy(){supr.onDstroy();mHandlr.rmovCallbacksAndMssags(null);}经过上述两个解决方案,在Handlr里的内存泄露问题就不会再出现了!
.总结本文总结的是关于Handlr的一些小事:内存泄露,阅读完本文后相信你已经懂得Handlr内存泄露的原理和详细的解决方案
接下来,我会继续讲解Android开发中关于Handlr和多线程的知识,包括Handlr源码、继承Thrad类、实现Runnabl接口、Handlr等等,有兴趣可以继续北京白癜风治疗最好的医院白癜风土方治疗办法