这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识、前端、后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过!

android使用java作为其开发环境。java的跨平台和垃圾回收机制已经帮助我们解决了底层的一些问题。但是尽管有了垃圾回收机制,在开发android的时候仍然时不时的遇到out of memory的问题,这个时候我们不禁要问,垃圾回收机器去哪儿了?

我们主要讲的是handler引起的泄漏,并给出三种解决办法,其中最后一种方法就是我们想介绍的WeakHandler 库。

可能导致泄漏问题的handler一般会被提示 Lint警告:

This Handler class should be static or leaks might occur 意思:class使用静态声明否者可能出现内存泄露。

这是一个基本的activity。在handler的post方法中我们加入了一个匿名的runnable,同时我将其执行延迟了整整80秒。我们运行这个程序,并且旋转几次手机,然后分析内存。

现在内存中有7个activity了,这太不靠谱了,所以我们来研究下为什么GC没有清理它。(上图中我查询内存中activity列表时用的是oql(对象查询语言),简单强大的工具,ps 怎么用的,谁能告诉我?)

从上图中我们可以看到其中一个对mainactivity的引用是来自this$0this$0是什么呢?以下是关于this$0的解释:

-------什么是this$0---------

非static的inner class里面都会有一个this$0的字段保存它的父对象。在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。一个编译后的inner class 很可能是这样的:

1
2
3
4
5
6
7
8
9
class parent$inner
{
synthetic parent this$0;
parent$inner(parent this$0)
{
this.this$0 = this$0;
this$0.foo();
}
}

-------什么是this$0结束---------

在我们的代码中,匿名的runnable是一个非静态的内部类,因此他会使用this$0来保存MainActivity,然后runnable会继续被它的callback引用,而callback又接着被接下来一连串的message引用,这样主线程的引用就太他妈多了。 当Activity finish后,延时消息会继续存在主线程消息队列中80秒,然后处理消息,因此handler在继续存在于内存中,而handler引用了Activity,在我们旋转手机的时候,Activity 不停的重建和finish,导致多个activity的引用出现。

一旦将Runnable或者是Message 发送进handler,将保存一连串的引用了主线程(这里是MainActivity吧)的Message命令,直到message执行完。如果发送Runnable设置了延迟时间,那么至少在这段延迟时间内内存泄漏是肯定的,如果是直接发送,在Message比较大的情况下,也是有可能发生暂时的泄漏的。

解决办法一:使用Static

再次执行,同时旋转手机,分析内存如下:

尼玛,还是一样的。我们看看是谁还拉着activity不放:

在最底下我们发现activity继续被DoneRunnable里面mTextView中的mContext引用着。看来在这种情况下,看来仅仅使用static并没有解决问题啊。还需要做点工作才行。

静态的Runnable加WeakReference

既然是因为mTextView引起的,那我们把mTextView换成弱引用好了:

需要注意的,既然mTextView是弱引用,所以随时都可能为null,因此需要在使用前判断是否为空。好了继续看看内存的情况:

all right,我想我们已经完美的解决问题了。总结一下我们做了哪些工作:

使用静态的内部类

对所有handler/Runnable中的变量都用弱引用。

但是这种方式代码是不是很多,而且还必须得小心翼翼。

在onDestroy中清理掉所有Messages

Handler有个很方便的方法:removeCallbacksAndMessages,当参数为null的时候,可以清除掉所有跟次handler相关的Runnable和Message,我们在onDestroy中调用次方法也就不会发生内存泄漏了。

运行,旋转手机

但是如果你对代码有更高的要求,觉得这样还不方便可以使用作者提供的WeakHandler

WeakHandler

WeakHandler使用起来和handler一模一样,但是他是安全的,WeakHandler使用如下:

你只需要把以前的Handler替换成WeakHandler就行了。

WeakHandler的实现原理

WeakHandler的思想是将Handler和Runnable做一次封装,我们使用的是封装后的WeakHandler,但其实真正起到handler作用的是封装的内部,而封装的内部对handler和runnable都是用的弱引用。

第一幅图是普通handler的引用关系图

第二幅图是使用WeakHandler的引用关系图

其实原文有对WeakHandler更多的解释,但是表述起来也挺复杂的。

原文地址:https://techblog.badoo.com/blog/2014/10/09/calabash-android-query/

github项目地址:https://github.com/badoo/android-weak-handler

Android Weak Handler:可以避免内存泄漏的Handler库的更多相关文章

  1. Handler系列之内存泄漏

    本篇简单的讲一下平常使用Handler时造成内存泄漏的问题. 什么是内存泄漏?大白话讲就是分配出去的内存,回收不回来.严重会导致内存不足OOM.下面来看一下造成内存泄漏的代码: public clas ...

  2. 安卓android WebView Memory Leak WebView内存泄漏

    Android WebView Memory Leak WebView内存泄漏 在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面 ...

  3. Android性能优化之避免内存泄漏的建议

    在android程序开发中,内存泄漏问题是比较常见的问题,相信有过一些android编程经历的程序猿都遇到过各种各样的内存泄漏.内存泄漏是造成应用程序OOM的主要原因之一,是编程中必须避免的问题.下面 ...

  4. android WeakReference(弱引用 防止内存泄漏)与SoftReference(软引用 实现缓存机制(cache))

    在Android开发中,基本上很少有用到软引用或弱引用,这两个东东若用的很好,对自己开发的代码质量的提高有很大的帮助.若用的不好,会坑了自己.所以,在还没有真正的去了解它们之前,还是慎用比较好. 下面 ...

  5. Android WebView Memory Leak WebView内存泄漏

    在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面后,即使在包含该webview的Activity的destroy()方法中,使用 ...

  6. android Context 持有导致的内存泄漏

    Context使用场景 为了防止Activity,Service等这样的Context泄漏于一些生命周期更长的对象,可以使用生命周期更长的ApplicationContext,但是不是所有的Conte ...

  7. 处理handler中的内存泄漏

    package de.bvb.test; import android.app.Activity; import android.os.Bundle; import android.os.Handle ...

  8. BaseHandler的封装, 处理handler中的内存泄漏

    package de.bvb.study.common; /** * 用于规范 Message.what此属性,避免出现魔法数字 */ public final class What { public ...

  9. Android实战技巧之三十八:Handler使用中可能引发的内存泄漏

    问题描写叙述 曾几何时,我们用原来的办法使用Handler时会有以下一段温馨的提示: This Handler class should be static or leaks might occur ...

随机推荐

  1. GNU libc (Glibc) 2.18 发布

    标准C库Glibc发布2.18正式版.2013-08-12 上一个版本是2012-12-25的2.17. 详细改进:Version 2.18 * The following bugs are reso ...

  2. Java 中的反射机制

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

  3. 【Win10】解决 模拟器调试手机 错误-> 引导阶段... 无法找到指定路径......\2052\msdbgui.dll

    去弄几天的Web服务,又弄了几天的CefSharp,都是给其它组的同学做了一下支持,终于又可以回来玩下Win10啦. 今天想试一下UWP在手机上的效果,就找了台WP手机开始升级,结果下载速度遥遥无期, ...

  4. 数据库优化实践【TSQL篇】

    在前面我们介绍了如何正确使用索引,调整索引是见效最快的性能调优方法,但一般而言,调整索引只会提高查询性能.除此之外,我们还可以调整数据访问代码和TSQL,本文就介绍如何以最优的方法重构数据访问代码和T ...

  5. 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么

    导师安排我做一个小项目,其中涉及到利用Adapter作为ListView的适配器,为ListView提供数据.选中某一项后,要让这一项变成选中状态,也就是背景图片要换一下.下面我就用一个小例子来模拟. ...

  6. [汇编] 从键盘输入一个一位数字,然后响铃n声

    ; multi-segment executable file template. data segment ends stack segment dw dup() ends code segment ...

  7. Entity Framework后台采用分页方式取数据与AspNetPager控件的使用

    本文是一个对AspNetPager控件使用的笔记! 有关AspNetPager控件可以查看杨涛主页.这是一个开放的自定义ASP.NET控件,支持各种自定义的数据分页方式,使用很方便,而且功能也很强大, ...

  8. Backbone入门——开发第一个Backbone页面

    1. 功能描述在新建的html页面中,通过导入的backbone文件搭建一个简单的mvc结构.当用户进入该页时,id号为“divTip”的<div>元素中将显示“hello,backbon ...

  9. atitit.api设计 方法 指南 手册 v2 q929.docx

    atitit.api设计 方法 指南 手册 v2 q929.docx atitit.api设计原则与方法 1. 归一化(锤子钉子理论)1 1.1. 链式方法2 1.2. 规则5:建立返回值类型2 1. ...

  10. iOS开发——高级技术&蓝牙服务

    蓝牙服务 蓝牙 随着蓝牙低功耗技术BLE(Bluetooth Low Energy)的发展,蓝牙技术正在一步步成熟,如今的大部分移动设备都配备有蓝牙4.0,相比之前的蓝牙技术耗电量大大降低.从iOS的 ...