内存泄漏是每一个app都可能面临的问题,尤其是弹窗导致的内存泄漏更是屡见不见,那么我们应该如何解决这类问题呢
1. 什么是内存泄漏
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。这句话的解释是说,对于Java任意一个对象都有一个生命周期,其中当这个对象到了不可达阶段时,一般就可以用Java的GC内存回收机制进行处理了。假如这个对象已经不用了,但是这个对象一直被长期持有着,GC默认这类对象不能被回收,那么这类对象就一直不会被回收,因此就会发生内存泄漏。
内存泄漏的原因:我认为本质就是对象的生命周期不同,一个弱生命周期的对象,持有一个强生命周期的对象;当弱生命周期的对象消亡后,该对象持有的强生命周期的对象依旧存在,就会产生内存泄漏。
2. 应用场景
举一个例子,对于app中某一个页面,弹出一个弹窗,弹出弹窗后,用户跳转到另一个页面了,表面上从用户角度,该弹窗已经肉眼看不到了,可是假如弹窗一直没有diss,系统认为这个弹窗一直存在,不会进行GC回收,这就导致了内存泄漏。因为一般弹窗只需要弹出来一次,因此一般弹窗可以用单例模式来实现。
一:单例设计模式造成的内存泄漏:
单例设计模式我就不多说了,这个是最基本的设计模式,相信大家都会使用,但是时候我们在使用单例设计模式时没有注意到其中的细节,就会造成内存泄漏。
单例设计模式的静态特性会使他的生命周期和应用程序的生命周期一样长,这就说明了如果一个对象不在使用了,而这时单例对象还在持有该对象的引用,这时GC就会无法回收该对象,造成了内存泄露的情况。下面是错误的单例设计模式的代码:
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
1、如果我们传入的Context是Application的Context的话,就没有任何问题,因为Application的Context生命周期和应用程序生命周期一样长。
2、如果我们传入的Context是Activity的Context的话,这时如果我们因为需求销毁了该Activity的话,Context也会随着Activity被销毁,但是单例还在持有对该类对象的引用,这时就会造成内存泄漏
因此下面这种写法可能更加正确。
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
3. 检测内存泄漏的办法
目前移动端检测内存泄漏的框架有LeakCanary,开发者可以在gradle中添加该依赖
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
然后运行调试运行app,假如出现内存泄漏,用户界面会出现一个新的app图标
点击进入这个app
如果这个app里面有数据,说明存在内存泄漏,反之则没有。
4. 弹窗内存泄漏解决
目前网上有一种解决内存泄漏的办法,就是利用软引用,但是这种办法其实不太保险,因为并没有从根本解决内存泄漏。
SoftReference<Activity> activity = new SoftReference(context);
实际对于一个弹窗来说,我们更应该在弹窗结束后,补充消亡后的逻辑,避免内存泄漏。
public void dismissDialog() {
if (dialog == null) {
return;
}
try {
dialog.dismiss();
} catch (Exception e) {
Log.d("DialogUtils", "dismissDialog: " + e.getMessage());
}
dialog = null; // 将dialog.diss后,将dialog置空防止出现内存泄漏
}
然后在调用该弹窗的时候,引用弹窗的dissmiss方法,完成将dialog=null防止出现内存泄漏