避免 Android中Context引起的内存泄露
context是我们在编写android程序经常使用到的对象,意思为上下文对象。 常用的有activity的context还是有application的context。activity用来展示活动界面,包含了很多的视图,而视图又含有图片,文字等资源。在android中内存泄露很容易出现,而持有很多对象内存占用的activity更加容易出现内存泄露,开发者需要特别注意这个问题。
本文讲介绍android中context,更具体的说是activity内存泄露的情况,以及如何避免activity内存泄露,加速应用性能。
drawable引起的内存泄露
drawable引起内存泄露这个问题是比较隐晦,难以察觉的。在阅读了romain guy的avoiding memory leaks,结合grepcode查看源码才明白了。
在android系统中,当我们进行了屏幕旋转,默认情况下,会销毁掉当前的activity,并创建一个新的activity并保持之前的状态。在这个过程中,android系统会重新加载程序的ui视图和资源。假设我们有一个程序用到了一个很大的bitmap图像,我们不想每次屏幕旋转时都重新加载这个bitmap对象,最简单的办法就是将这个bitmap对象使用static修饰。
private static drawable sbackground; @override protected void oncreate(bundle state) { super.oncreate(state); textview label = new textview(this); label.settext("leaks are bad"); if (sbackground == null) { sbackground = getdrawable(r.drawable.large_bitmap); } label.setbackgrounddrawable(sbackground); setcontentview(label); }
但是上面的方法在屏幕旋转时有可能引起内存泄露,无论是咋一看还是仔细看这段代码,都很难发现哪里引起了内存泄露。
当一个drawable绑定到了view上,实际上这个view对象就会成为这个drawable的一个callback成员变量,上面的例子中静态的sbackground持有textview对象lable的引用,而lable只有activity的引用,而activity会持有其他更多对象的引用。sbackground生命周期要长于activity。当屏幕旋转时,activity无法被销毁,这样就产生了内存泄露问题。
2.3.7及以下版本drawable的setcallback方法的实现
public final void setcallback(callback cb) { mcallback = cb; }
好在从4.0.1开始,引入了弱引用处理这个问题,弱引用在gc回收时,不会阻止gc回收其指向的对象,避免了内存泄露问题。
public final void setcallback(callback cb) { mcallback = new weakreference<callback>(cb); }
单例引起的内存泄露
单例是我们比较简单常用的一种设计模式,然而如果单例使用不当也会导致内存泄露。 比如这样一个例子,我们使用饿汉式初始化单例,appsettings我们需要持有一个context作为成员变量,如果我们按照下面的实现其实是有问题。
public class appsettings { private context mappcontext; private static appsettings sinstance = new appsettings(); //some other codes public static appsettings getinstance() { return sinstance; } public final void setup(context context) { mappcontext = context; } }
sinstance作为静态对象,其生命周期要长于普通的对象,其中也包含activity,当我们进行屏幕旋转,默认情况下,系统会销毁当前activity,然后当前的activity被一个单例持有,导致垃圾回收器无法进行回收,进而产生了内存泄露。
解决的方法就是不持有activity的引用,而是持有application的context引用。代码如下修改
public final void setup(context context) { mappcontext = context.getapplicationcontext(); }
访问这里了解更多关于单例模式的问题
条条方法返回context
通常我们想要获取context对象,主要有以下四种方法
- view.getcontext,返回当前view对象的context对象,通常是当前正在展示的activity对象。
- activity.getapplicationcontext,获取当前activity所在的(应用)进程的context对象,通常我们使用context对象时,要优先考虑这个全局的进程context。
- contextwrapper.getbasecontext():用来获取一个contextwrapper进行装饰之前的context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
- activity.this 返回当前的activity实例,如果是ui控件需要使用activity作为context对象,但是默认的toast实际上使用applicationcontext也可以。
其他内存泄露问题
onsharedpreferencechangelistener详解及出现不触发解决办法
避免内存泄露须谨记
- 不要让生命周期长于activity的对象持有到activity的引用
- 尽量使用application的context而不是activity的context
- 尽量不要在activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用(具体可以查看细话java:”失效”的private修饰符了解)。如果使用静态内部类,将外部实例引用作为弱引用持有。
- 垃圾回收不能解决内存泄露,了解android中垃圾回收机制
参考文章
avoiding memory leaks
difference between getcontext() , getapplicationcontext() , getbasecontext() and “this”
android – what's the difference between the various methods to get a context?
以上就是对android context 内存泄漏的资料整理,后续继续添加相关资料,谢谢大家的支持!
上一篇: java必学必会之线程(1)