【干货】避免Android中Context引发的内存泄露


视频汇总首页:http://edu.51cto.com/lecturer/index/user_id-4626073.htmlhtml


Context是咱们在编写Android程序常常使用到的对象,意思为上下文对象。 经常使用的有Activity的Context仍是有Application的Context。Activity用来展现活动界面,包含了不少的视图,而视图又含有图片,文字等资源。在Android中内存泄露很容易出现,而持有不少对象内存占用的Activity更加容易出现内存泄露,开发者须要特别注意这个问题。
设计模式


本文讲介绍Android中Context,更具体的说是Activity内存泄露的状况,以及如何避免Activity内存泄露,加速应用性能。微信


Drawable引发的内存泄露app


Drawable引发内存泄露这个问题是比较隐晦,难以察觉的。在阅读了Romain Guy的Avoiding memory leaks,结合grepcode查看源码才明白了。ide


在Android系统中,当咱们进行了屏幕旋转,默认状况下,会销毁掉当前的Activity,并建立一个新的Activity并保持以前的状态。在这个过程当中,Android系统会从新加载程序的UI视图和资源。假设咱们有一个程序用到了一个很大的Bitmap图像,咱们不想每次屏幕旋转时都从新加载这个Bitmap对象,最简单的办法就是将这个Bitmap对象使用static修饰。性能


private static Drawable sBackground;
this


@Overridespa

protected void onCreate(Bundle state) {设计

super.onCreate(state);code


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也能够。


其余内存泄露问题


  • Android中糟糕的AsyncTask

  • Android中Handler引发的内存泄露

  • Google为什么这样设计OnSharedPreferenceChangeListener


避免内存泄露须谨记


  • 不要让生命周期长于Activity的对象持有到Activity的引用

  • 尽可能使用Application的Context而不是Activity的Context

  • 尽可能不要在Activity中使用非静态内部类,由于非静态内部类会隐式持有外部类实例的引用(具体能够查看细话Java:”失效”的private修饰符了解)。若是使用静态内部类,将外部实例引用做为弱引用持有。

  • 垃圾回收不能解决内存泄露,了解Android中垃圾回收机制

微信订阅号

wKiom1XCvSHCrSJRAAC1mdY8jWo590.jpg

相关文章
相关标签/搜索