当前位置: 移动技术网 > IT编程>移动开发>Android > 避免 Android中Context引起的内存泄露

避免 Android中Context引起的内存泄露

2019年07月24日  | 移动技术网IT编程  | 我要评论

中餐厅 周冬雨 离开,汾阳中学网络办公系统,斗战神鹰愁涧怎么去

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对象,主要有以下四种方法

  1. view.getcontext,返回当前view对象的context对象,通常是当前正在展示的activity对象。
  2. activity.getapplicationcontext,获取当前activity所在的(应用)进程的context对象,通常我们使用context对象时,要优先考虑这个全局的进程context。
  3. contextwrapper.getbasecontext():用来获取一个contextwrapper进行装饰之前的context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
  4. activity.this 返回当前的activity实例,如果是ui控件需要使用activity作为context对象,但是默认的toast实际上使用applicationcontext也可以。

其他内存泄露问题

android中糟糕的asynctask

android中handler引起的内存泄露

onsharedpreferencechangelistener详解及出现不触发解决办法

避免内存泄露须谨记

  1. 不要让生命周期长于activity的对象持有到activity的引用
  2. 尽量使用application的context而不是activity的context
  3. 尽量不要在activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用(具体可以查看细话java:”失效”的private修饰符了解)。如果使用静态内部类,将外部实例引用作为弱引用持有。
  4. 垃圾回收不能解决内存泄露,了解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 内存泄漏的资料整理,后续继续添加相关资料,谢谢大家的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网