关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android中Handler引起的内存泄露

    Android中Handler引起的内存泄露

    2015-01-05 00:00:00 出处:xiaanming的博客
    分享

    在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用。通常我们的代码会这样实现。

    public class SampleActivity extends Activity {
    
      private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          // ... 
        }
      }
    }

    但是,其实上面的代码可能导致内存泄露,当你使用Android lint工具的话,会得到这样的警告

    In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class

    看到这里,可能还是有一些搞不清楚,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下。

    1.当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper的主要工作就是一个一个处理消息队列中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。主线程中的Looper生命周期和当前应用一样长。

    2.当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用Handler#handleMessage(Message)完成消息的正确处理。

    3.在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。关于这一内容可以查看Java中”失效”的private修饰符解析

    确实上面的代码示例有点难以察觉内存泄露,那么下面的例子就非常明显了

    public class SampleActivity extends Activity {
    
      private final Handler mLeakyHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          // ...
        }
      }
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // Post a message and delay its execution for 10 minutes.
        mLeakyHandler.postDelayed(new Runnable() {
          @Override
          public void run() { /* ... */ }
        }, 1000 * 60 * 10);
    
        // Go back to the previous Activity.
        finish();
      }
    }

    分析一下上面的代码,当我们执行了Activity的finish方法,被延迟的消息会在被处理之前存在于主线程消息队列中10分钟,而这个消息中又包含了Handler的引用,而Handler是一个匿名内部类的实例,其持有外面的SampleActivity的引用,所以这导致了SampleActivity无法回收,进行导致SampleActivity持有的很多资源都无法回收,这就是我们常说的内存泄露。

    注意上面的new Runnable这里也是匿名内部类实现的,同样也会持有SampleActivity的引用,也会阻止SampleActivity被回收。

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

    public class SampleActivity extends Activity {
    
      /**
       * Instances of static inner classes do not hold an implicit
       * reference to their outer class.
       */
      private static class MyHandler extends Handler {
        private final WeakReference<SampleActivity> mActivity;
    
        public MyHandler(SampleActivity activity) {
          mActivity = new WeakReference<SampleActivity>(activity);
        }
    
        @Override
        public void handleMessage(Message msg) {
          SampleActivity activity = mActivity.get();
          if (activity != null) {
            // ...
          }
        }
      }
    
      private final MyHandler mHandler = new MyHandler(this);
    
      /**
       * Instances of anonymous classes do not hold an implicit
       * reference to their outer class when they are "static".
       */
      private static final Runnable sRunnable = new Runnable() {
          @Override
          public void run() { /* ... */ }
      };
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // Post a message and delay its execution for 10 minutes.
        mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
    
        // Go back to the previous Activity.
        finish();
      }
    }

    其实在Android中很多的内存泄露都是由于在Activity中使用了非静态内部类导致的,就像本文提到的一样,所以当我们使用时要非静态内部类时要格外注意,假如其实例的持有对象的生命周期大于其外部类对象,那么就有可能导致内存泄露。个人倾向于使用文章的静态类和弱引用的方法解决这种问题。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Edge浏览器百度被劫持/篡改怎么办,地址后边跟着尾巴#tn=68018901_7_oem_dg

    Google Chrome 在 iPhone 上新增了 Safari 数据导入选项

    Windows 11专业版 KMS工具激活产品密钥的方法

    DEDECMS安全策略官方出品

    Microsoft Text Input Application 可以关闭吗?

    新版本QQ如何关闭自带的浏览器?

    C++编程语言中continue的用法和功能,附举例示范代码

    c++ map 的数据结构、基本操作以及其在实际应用中的使用。

    C语言如何避免内存泄漏、缓冲区溢出、空指针解引用等常见的安全问题

    C语言中的break语句详解

    IT头条

    马斯克2026最新采访总结:2040年,全球机器人数量将突破100亿台

    23:52

    专家解读|规范人工智能前沿业态健康发展的新探索:解读《人工智能拟人化互动服务管理暂行办法》

    00:54

    用至强 6高存力搞定MoE卸载!

    17:53

    美国将允许英伟达向中国“经批准的客户”出售H200 GPU

    02:08

    苹果与微信就15%手续费达成一致?腾讯未置可否

    22:00

    技术热点

    PHP 和 Node.js 的10项对比挑战

    Javascript闭包深入解析及实现方法

    windows 7、windows 8.1手动增加右键菜单功能技巧

    MYSQL出错代码大汇总

    windows 7假死机怎么办 windows 7系统假死机的原因以及解决方法

    Ubuntu(Linux)下配置IP地址的方法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。