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

  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 内存泄漏的资料整理,后续继续添加相关资料,谢谢大家的支持!

(0)

相关推荐

  • Android 中Context的使用方法详解

    Android 中Context的使用方法详解 概要: Context字面意思是上下文,位于framework package的android.content.Context中,其实该类为LONG型,类似Win32中的Handle句柄.很多方法需要通过 Context才能识别调用者的实例:比如说Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的实例为Activity,而到了一个button的onClick(View view)等方法时,我们用t

  • 深入解析Android App开发中Context的用法

    Context在开发Android应用的过程中扮演着非常重要的角色,比如启动一个Activity需要使用context.startActivity方法,将一个xml文件转换为一个View对象也需要使用Context对象,可以这么说,离开了这个类,Android开发寸步难行,对于这样一个类,我们又对他了解多少呢.我就说说我的感受吧,在刚开始学习Android开发时,感觉使用Context的地方一直就是传入一个Activity对象,久而久之感觉只要是Context的地方就传入一个Activity就行

  • android基础教程之context使用详解

    在android中有两种context,一种是application context,一种是activity context,通常我们在各种类和方法间传递的是activity context. 区别联系: 复制代码 代码如下: public class MyActivity extends Activity {    public void method() {       mContext = this;    // since Activity extends Context       m

  • Android获取其他包的Context实例代码

    Android中有Context的概念,想必大家都知道.Context可以做很多事情,打开activity.发送广播.打开本包下文件夹和数据库.获取classLoader.获取资源等等.如果我们得到了一个包的Context对象,那我们基本上可以做这个包自己能做的大部分事情.那我们能得到吗?很高兴的告诉你,能!Context有个createPackageContext方法,可以创建另外一个包的上下文,这个实例不同于它本身的Context实例,但是功能是一样的. 这个方法有两个参数:1.packag

  • Android编程实现为ListView创建上下文菜单(ContextMenu)的方法

    本文实例讲述了Android编程实现为ListView创建上下文菜单(ContextMenu)的方法.分享给大家供大家参考,具体如下: ContextMenu称为上下文菜单,一般在控件上长按时弹出.今天我们学习ContextMenu的用法,这里与listview相结合,先在ListView显示几个Item,然后在Item上长按,弹出一个菜单(就是ContextMenu),点击菜单上的项目,提示刚才长按的Item的Position. main.xml文件 <?xml version="1.0

  • Android全局获取Context实例详解

    Android全局获取Context实例详解 在弹出Toast 启动活动 发送广播 操作数据库 使用通知等等时都需要Context 如果操作在活动中进行是很简单的,因为活动本身就是一个Context对象 但是当逻辑代码脱离了Activity类,此时使用Context就需要一些技巧了: 我们可以定制一个自己的Application类,以便管理程序内一些全局状态信息,比如全局Context 代码如下: public class MyApplication extends Application{ p

  • 谈谈Android里的Context的使用实例

    大家好,今天给大家分享一下Android里的Context的一些用法,以前经常有人在群里问我比如我在一个工具类里的某个方法,或者View里需要调用Context.但是工具类还有View里没有这个上下文怎么办?为了解决大家的疑问,为了解决大家的疑问,我今天写一个简单的Demo.让大家如何学好自如的用Context.想什么时候有Context,什么时候就有Context. 这里大致可以分为两种:一是传递Context参数,二是调用全局的Context. 其实我们应用启动的时候会启动Applicati

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

    Context是我们在编写Android程序经常使用到的对象,意思为上下文对象. 常用的有Activity的Context还是有Application的Context.Activity用来展示活动界面,包含了很多的视图,而视图又含有图片,文字等资源.在Android中内存泄露很容易出现,而持有很多对象内存占用的Activity更加容易出现内存泄露,开发者需要特别注意这个问题. 本文讲介绍Android中Context,更具体的说是Activity内存泄露的情况,以及如何避免Activity内存泄

  • Android 中Handler引起的内存泄露

    在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.通常我们的代码会这样实现. public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } } 但是,其实上面的代码可能导致内存泄露,当你使用Androi

  • Android中Handler引起的内存泄露问题解决办法

    在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.通常我们的代码会这样实现. 复制代码 代码如下: public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() {     @Override     public void handleMessage(Message msg) {       // ...     }   }

  • android中Context深入详解

    以下分别通过Context认知角度,继承关系,对象创建等方面android中Context做了深入的解释,一起学习下. 1.Context认知. Context译为场景,一个应用程序可以认为是一个工作环境,在这个工作环境中可以存在许多场景,coding代码的场景 ,打电话的场景,开会的场景.这些场景可以类比不同的Activity,service. 2.从两个角度认识Context. 第一:Activity继承自Context,同时Activity还实现了其他的interface,我们可以这样看,

  • Android垃圾回收机制解决内存泄露问题

    在android编码中,会有一些简便的写法和编码习惯,会导致我们的代码有很多内存泄露的问题,在这里做一个已知错误的总结: 1.编写单例的时候常出现的错误. 错误方式: public class Foo{ private static Foo foo; private Context mContext; private Foo(Context mContext){ this.mContext = mContext; } // 普通单例,非线程安全 public static Foo getInst

  • Android 中对于图片的内存优化方法

    1. 对图片本身进行操作 尽量不要使用 setImageBitmap.setImageResource. BitmapFactory.decodeResource 来设置一张大图,因为这些方法在完成 decode 后,最终都是通过 Java 层的 createBitmap 来完成的,需要消耗更多内存.因此,改用先通过 BitmapFactory.decodeStream 方法,创建出一个 bitmap,再将其设为 ImageView 的 source,decodeStream 最大的秘密在于其直

  • 解决Android使用Handler造成内存泄露问题

    一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收:另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收. Android中使用Handler造成内存泄露的原因 private Han

  • 详解Android使用Handler造成内存泄露的分析及解决方法

    一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收:另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收. Android中使用Handler造成内存泄露的原因 private Han

  • Android编程中避免内存泄露的方法总结

    Android的应用被限制为最多占用16m的内存,至少在T-Mobile G1上是这样的(当然现在已经有几百兆的内存可以用了--译者注).它包括电话本身占用的和开发者可以使用的两部分.即使你没有占用全部内存的打算,你也应该尽量少的使用内存,以免别的应用在运行的时候关闭你的应用.Android能在内存中保持的应用越多,用户在切换应用的时候就越快.作为我的一项工作,我仔细研究了Android应用的内存泄露问题,大多数情况下它们是由同一个错误引起的,那就是对一个上下文(Context)保持了长时间的引

随机推荐