当前位置: 首页 > news >正文

Android中的内部类引起的内存泄露

引子

什么是内部类?什么是内存泄露?为什么Android的内部类容易引起内存泄露?如何解决?


什么是内部类?

什么是内部类?什么又是外部类、匿名类、局部类、顶层类、嵌套类?大家可以参考我这篇文章 ,再查查一些资料,先弄清楚什么是内部类和内部类的特性再向下看。

经常会遇见Android程序中这样使用handler:

public class SomeActivity {

    // ......
    
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
            case 0:
                // do something
                break;
            case 1:
                // do something
                break;
            default:
                break;
            }
        }
    };
    
    private void someMethod () {
        mHandler.sendEmptyMessage(0);
    }
}

上述代码中,mHandler字段指向一个匿名Handler类。匿名类是内部类吗?匿名类会持有外部类的对象吗? 答案是:匿名类是内部类,但是是特殊的内部类,如果把匿名类放到一个static方法中,它是不会持有外部类实例的。而在上面的代码中,这个mHandler会持有外部类(SomeActivity)实例的引用,因为它处于一个对象的上下文中,而不是类型上下文中。

什么是”持有外部类实例的引用“?你可以这么理解:

public class InnerClass {
    private OuterClass outer;
    public InnerClass(OuterClass outer) {
        this.outer = outer;
    }
}

就是说,创建InnerClass对象的时,必须传递进去一个OuterClass对象,赋值给InnerClass的一个字段outer,该字段是OuterClass对象的引用。回忆一下GC原理,如果InnerClass对象没有被标记为垃圾对象,那么outer指向的OuterClass对象会可能被标记为垃圾对象吗?答案是:InnerClass对象与GC Root有引用路径,InnerClass对象又引用了OuterClass对象,那么OuterClass对象到GC Root也是有引用路径的,所以,OuterClass不可能是垃圾对象。


为什么发生内存泄露?

由上文可以看出:当mHandler没有被回收时,其外围Activity对象不能被回收。当Activity被用户关闭(finish),而此时mHandler还未被回收,那么Activity对象就不会被回收,造成Activity内存泄露。

问题的关键转入到了这个问题:为什么Activity被finish了,mHandler还不能被回收?

发送消息时,我们使用了这个函数:mHandler.sendEmptyMessage(0)函数。通过查看源码追踪调用关系,发现走到了:

Message对象有个target字段,该字段是Handler类型,引用了当前Handler对象。一句话就是:你通过Handler发往消息队列的Message对象持有了Handler对象的引用。假如Message对象一直在消息队列中未被处理释放掉,你的Handler对象就不会被释放,进而你的Activity也不会被释放。

这种现象很常见,当消息队列中含有大量的Message等待处理,你发的Message需要等几秒才能被处理,而此时你关闭Activity,就会引起内存泄露。如果你经常send一些delay的消息,即使消息队列不繁忙,在delay到达之前关闭Activity也会造成内存泄露。


有什么解决方案?

方案#1:在关闭Activity时(finish/onStop等函数中),取消还在排队的Message:
mHandler.removeCallbacksAndMessages(null);

方案#2:使用WeakReference截断StrongReference。问题的症结既然是内部类持有外部类对象的引用,那我不用内部类就行了,直接使用静态成员类。但mHandler又需要与Activity对象交互,那就来个WeakReference,指向外部Activity对象。

public class SomeActivity {
    private Handler mHandler = new MyHandler(this);
    private static class MyHandler extends Handler {
        private WeakReference<SomeActivity> ref;
        public MyHandler(SomeActivity activity) {
            if (activity != null) {
                ref = new WeakReference<SomeActivity>(activity);
            }
        }
        @Override
        public void handleMessage(Message msg) {
            if (ref == null) {
                return;
            }
            SomeActivity v = ref.get();
            if (v == null) {
                return;
            }
            // handle message
        }
    }
}

当Activity想关闭销毁时,mHandler对它的弱引用没有影响,该销毁销毁;当mHandler通过WeakReference拿不到Activity对象时,说明Activity已经销毁了,就不用处理了,相当于丢弃了消息。

另外,当你使用Handler有内存泄露时候,Android Studio的Lint会有如下提示:

 

相关文章:

  • XMemcached实例及与spring的集成
  • JAVA8
  • classpath对获取配置文件的影响
  • Delphi XE里面 SharedActivityContext 提示错误
  • Android 带你从源码的角度解析Scroller的滚动实现原理
  • ODI调用WebService----OdiInvokeWebService
  • OK335xS GPMC nand device register hacking
  • WebService的缓存机制
  • Linux压缩打包方法连载之三:bzip2, bzcat 命令
  • 用MonoDevelop开发Linux游戏
  • Android 服务类Service 的详细学习
  • Android获取返回键的点击事件
  • 赢得Docker挑战最佳实践
  • 小蚂蚁学memcache笔记—php操作memcache(2)
  • ipv6现状,加英文的中括号访问, ipv6测试http://test-ipv6.com
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • angular2开源库收集
  • AngularJS指令开发(1)——参数详解
  • Flex布局到底解决了什么问题
  • Git学习与使用心得(1)—— 初始化
  • JavaScript函数式编程(一)
  • java多线程
  • MobX
  • Mybatis初体验
  • rc-form之最单纯情况
  • React-Native - 收藏集 - 掘金
  • SpiderData 2019年2月13日 DApp数据排行榜
  • spring cloud gateway 源码解析(4)跨域问题处理
  • 和 || 运算
  • 缓存与缓冲
  • 前端_面试
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 悄悄地说一个bug
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 微信小程序开发问题汇总
  • 我是如何设计 Upload 上传组件的
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • const的用法,特别是用在函数前面与后面的区别
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #Linux(make工具和makefile文件以及makefile语法)
  • #pragma once
  • #前后端分离# 头条发布系统
  • %@ page import=%的用法
  • (04)odoo视图操作
  • (12)Linux 常见的三种进程状态
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (6)STL算法之转换
  • (day6) 319. 灯泡开关
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (安卓)跳转应用市场APP详情页的方式
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (二)构建dubbo分布式平台-平台功能导图