一:匿名内部类/非静态内部类
匿名内部类的泄漏原因:匿名内部类会隐式地持有外部类的引用.当外部类被销毁时,内部类并不会自动销毁,因为内部类并不是外部类的成员变量,
它们只是在外部类的作用域内创建的对象,所以内部类的销毁时机和外部类的销毁时机是不同的,
所以会不会取决与对应对象是否存在被持有的引用.
案例一:有名内部类导致内存泄漏
匿名内部类/非静态内部类,Android开发经常会继承实现 Activity 或者 Fragment 或者 View。
如果你使用了匿名类,而又被异步线程所引用,那得小心,如果没有任何措施同样会导致内存泄漏的.
runnable1 和 runnable2 的区别就是,runnable2 使用了匿名内部类,我们看看引用时的引用内存:
可以看到,runnable1 是没有什么特别的。但 runnable2 多出了一个 MainActivity 的引用,若是这个引用再传入到一个异步线程,此线程在和Activity生命周期不一致的时候,也就造成了Activity的泄露。
在 MainActivity 内部创建了一个非静态内部类的单例TestInnerBad,不过这种写法却会造成内存泄漏,非静态内部类默认会持有外部类MainActivity 的引用,导致 MainActivity的内存资源不能正常回收.
解决方案:
将该内部类TestInnerBad设为静态内部类或将该内部类抽取出来封装成一个单例,
案例二:匿名内部类导致内存泄漏
public class TestActivity {public static void main(String[] args) {//java的匿名内部类 new Thread(new Runnable() {@Overridepublic void run() {}}).start();}
}
匿名内部类Runnable持有外部类TestActivity 的引用, 当外部类TestActivity销毁时,匿名内部类Runnable 会导致内存泄漏,
解决方案:
创建一个静态内部类实现Runnable接口或者使用lambda表达式
public class TestActivity {Runnable runnable1public static void main(String[] args) {runnable1 = new MyRunnable()new Thread(runnable1).start();}//创建一个静态内部类
private static class MyRunnable implements Runnable{@Overridepublic void run(){
}
}@overRide
onDestroy(){runnable1=null }}
java: 把Runnable转为lambda写法 避免了内存泄漏 new Thread(() -> { }).start();
kotlin: 把Runnable转为lambda写法 避免了内存泄漏Thread({}).start()
或者
Thread {}.start()
案例三:匿名内部类导致内存泄漏
public class MyActivity extends Activity {private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);button = new Button(this);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// do something}});setContentView(button);}
}
匿名内部类OnClickListener持有了外部类MyActivity的引用,如果MyActivity被销毁之前,button没有被清除,
就会导致MyActivity无法被垃圾回收。(此处可以将Button 看作是自己定义的一个对象,
一般解法是将button对象置为空)
注意事项:在Activity销毁时,应该将所有持有Activity引用的对象设置为null。
二:静态变量
当我们的成员变量是 static 的时候,那么它的生命周期将和整个app的生命周期一致。这必然会导致一系列问题,
解决方案:
不要在类初始时初始化静态成员。可以考虑 lazy初始化(延迟加载)
三:单例Singleton
单例的静态特性,使得它的生命周期和应用的生命周期会一样长,所以一旦使用有误,小心无限制的持有Activity的引用而导致内存泄漏。
public class MySingleton {private static MySingleton instance;private Context context;private MySingleton(Context context){this.context=context
}public static MySingleton getInstance(Context context) {if (instance == null) {instance = new MySingleton(Context context);}return instance;}// ...
}
如果我们传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出的时候,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会回收,这造成的内存泄漏
解决方案: 使用 Application 的 Context 避免内存泄漏
public class MySingleton {private static MySingleton instance;private Context context;private MySingleton(Context context){// this.context=context// 使用 Application 的 Context 避免内存泄漏this.context=context.getApplicationContext()}public static MySingleton getInstance(Context context) {if (instance == null) {synchronized (MySingleton.class) {if (instance == null) {instance = new MySingleton(Context context);}}}return instance;}public static void releaseInstance() {instance = null;}// ...
}
四:Context
如果需要使用 Context,推荐的使用 Application 的 Context。当然,Application 的 context 不是万能的,所以也不能随便乱用,对于有些地方则必须使用 Activity 的 Context,对于Application,Service,Activity三者的 Context 的应用场景如下: