android 7.1内存泄漏,Android进阶7:Handler内存泄漏详解及其解决方案

在android开发过程中,我们可能会遇到过令人奔溃的OOM异常,面对这样的异常我们是既熟悉又深恶痛绝的,因为造成OOM的原因有很多种情况,如加载图片过大,某已不再使用的类未被GC及时回收等等……本篇我们就来分析其中一种造成OOM的场景,它就是罪恶的内存泄漏。对于这样的称呼,我们并不陌生,甚至屡次与之"并肩作战",只不过它就是一个猪队友,只会不断送塔…….

本篇分为3部分:

1.Handler内存泄漏例子说明以及原理阐明

2.问题验证(如果感觉繁琐请直接跳过)

3.Handler内存泄漏解决方法

eg.Handler内存泄漏例子说明以及原理阐明

1.Handler内存泄漏例子说明以及原理阐明

Handler,我们已经相当熟悉了,而且经常用得不亦乐乎,但就是因为太熟悉了,才会偶尔被它反捅一刀,血流不止……还记得我们曾经满怀信心地使用着如下的优美而又简洁的代码不?

29ca2ff45b4e75655ef7fe65f8583d6d.pnghandler代码

不怕你吓着,实话告诉你,这个代码已经造成内存泄漏了!!!不相信?我们使用Android lint工具检测一下该类的代码:

ffedc9e78c0e777aab2831e554beafe1.pngAndroid lint

面对现实吧,那为什么会这样呢?在java中非静态内部类和匿名内部类都会隐式持有当前类的外部引用,由于Handler是非静态内部类所以其持有当前Activity的隐式引用,如果Handler没有被释放,其所持有的外部引用也就是Activity也不可能被释放,当一个对象一句不需要再使用了,本来该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏(上面的例子就是这个原因)。最终也就造成了OOM…….我们再来段清晰的代码,我们来使用mHandler发送一个延迟消息:

cf288ca6ce4f00da753ae15bb8573b88.pngenter description here

分析:当我们执行了HandlerActivity的界面时,被延迟的消息会在被处理之前存在于主线程消息队列中5分钟,而这个消息中又包含了Handler的引用,而我们创建的Handler又是一个匿名内部类的实例,其持有外部HandlerActivity的引用,这将导致了HandlerActivity无法回收,进行导致HandlerActivity持有的很多资源都无法回收,从而就造成了传说中的内存泄露问题!

2.问题验证(如果感觉繁琐请直接跳过)

为了进一步验证内存泄漏问题,我们在该类中创建一个int数组,该数组分配的内存大小为2m,然后我们用DDMS来查看heap内存,然后使用GC回收,看看内存会不会有变化:

8f86ee311848979818743c19c15b40cd.pngenter description here

第一次启动app时,head内存为12.5M,已经分配内容(Allocated):8.5M,空闲内存:4M,我们频繁点击GC按钮,内存并没有发生明显变化,现在我们点击手机返回健,推出应用,然后再重新进入,同样检测一下head内存:

435521f70ec4b4985520f984a4c80e2d.pngenter description here

我们发现head内存:20.5M,Allocated:16.5M,Free:4M,堆内存和已经分配内存近乎翻倍,我们继续频繁点击GC, 看看能否被回收?结果内存并没有明显变化,现在我们继续点击手机返回健,推出应用,然后再重新进入,同样再次检测一下head内存:

435521f70ec4b4985520f984a4c80e2d.pngenter description here

我们发现head内存:28.5M,Allocated:24.5M,Free:4M,堆内存和已经分配内存又增加了,而且无论我们如何点击GC回收内存,内存都没有明显变化,而且每启动一次该页面,内存就增加一倍!这也就说存在在某个类只创建而没销毁的情况,其实就是存在内存泄漏问题。我们使用MAT工具进一步验证这个问题,我们来看一组Histogram的数据和dominator tree数据,首先是Histogram的数据:

43633abd8ab6dcf9a6b4a1d551798005.pngHistogram的数据

dominator tree数据:

2d1ed5f62a6ac932bf060ed60ba3d890.pngdominator tree数据

同时存在三个一样的HandlerActivity和内部类,这就足以说明HandlerActvity只有创建没被销毁了吧,也就是说Handler造成的内存泄漏真的存在。

3.Handler内存泄漏解决方法

解决这个问题思路就是使用静态内部类并继承Handler时(或者也可以单独存放成一个类文件)。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。另外关于同样也需要将Runnable设置为静态的成员属性。修改后不会导致内存泄露的代码如下:

方法:声明静态匿名内部类+弱引用

public class HandlerActivity extends Activity{

//创建一个2M大小的int数组

int[] datas=new int[1024*1024*2];

//    Handler mHandler = new Handler(){

//        @Override

//        public void handleMessage(Message msg) {

//            super.handleMessage(msg);

//        }

//    };

/**

* 创建静态内部类

*/

private static class MyHandler extends Handler{

//持有弱引用HandlerActivity,GC回收时会被回收掉.

private final WeakReference mActivty;

public MyHandler(HandlerActivity activity){

mActivty =new WeakReference(activity);

}

@Override

public void handleMessage(Message msg){

HandlerActivity activity=mActivty.get();

super.handleMessage(msg);

if(activity!=null){

//执行业务逻辑

}

}

}

private static final Runnable myRunnable = new Runnable() {

@Override

public void run(){

//执行我们的业务逻辑

}

};

@Override

protected void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_handler_leak);

MyHandler myHandler=new MyHandler(this);

//解决了内存泄漏,延迟5分钟后发送

myHandler.postDelayed(myRunnable, 1000 * 60 * 5);

}

}

复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Studio内存泄漏是指在应用程序中存在未释放的内存资源,导致内存占用不断增加,最终可能导致应用程序崩溃或者性能下降。以下是一些常见的Android Studio内存泄漏情况和解决方法: 1. 静态变量引用:如果在一个类中使用了静态变量,并且这个静态变量引用了一个Activity或者Context对象,那么当Activity被销毁时,这个对象仍然被引用,导致内存泄漏。解决方法是使用弱引用或者及时释放静态变量。 2. 匿名内部类引用:如果在一个Activity或者Fragment中使用了匿名内部类,并且这个内部类引用了外部类的实例,那么当Activity或者Fragment被销毁时,这个内部类仍然持有外部类的引用,导致内存泄漏。解决方法是使用静态内部类或者弱引用。 3. Handler引用:如果在一个Activity或者Fragment中使用了Handler,并且Handler没有被正确地释放,那么当Activity或者Fragment被销毁时,Handler仍然持有对Activity或者Fragment的引用,导致内存泄漏。解决方法是在Activity或者Fragment的生命周期方法中及时移除消息和回调。 4. 资源未关闭:如果在使用完资源后没有及时关闭,比如数据库连接、文件流等,会导致资源泄漏。解决方法是在适当的时候关闭资源,可以使用try-finally或者try-with-resources语句块来确保资源的正确关闭。 5. 单例模式引用:如果在应用程序中使用了单例模式,并且单例对象持有了Activity或者Context的引用,那么当Activity或者Context被销毁时,单例对象仍然持有对它们的引用,导致内存泄漏。解决方法是在单例对象中使用弱引用或者及时释放对Activity或者Context的引用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值