匿名内部类的泄漏原因:匿名内部类会隐式地持有外部类的引用.当外部类被销毁时,内部类并不会自动销毁,因为内部类并不是外部类的成员变量,
它们只是在外部类的作用域内创建的对象,所以内部类的销毁时机和外部类的销毁时机是不同的,
所以会不会取决与对应对象是否存在被持有的引用.
匿名内部类/非静态内部类,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() {
- @Override
- public void run() {
-
- }
- }).start();
- }
- }
匿名内部类Runnable持有外部类TestActivity 的引用, 当外部类TestActivity销毁时,匿名内部类Runnable 会导致内存泄漏,
解决方案:
创建一个静态内部类实现Runnable接口或者使用lambda表达式
- public class TestActivity {
- Runnable runnable1
-
- public static void main(String[] args) {
-
- runnable1 = new MyRunnable()
-
- new Thread(runnable1).start();
- }
-
- //创建一个静态内部类
- private static class MyRunnable implements Runnable{
- @Override
- public 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;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- button = new Button(this);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // do something
- }
- });
- setContentView(button);
- }
- }
匿名内部类OnClickListener持有了外部类MyActivity的引用,如果MyActivity被销毁之前,button没有被清除,
就会导致MyActivity无法被垃圾回收。(此处可以将Button 看作是自己定义的一个对象,
一般解法是将button对象置为空)
注意事项:在Activity销毁时,应该将所有持有Activity引用的对象设置为null。
当我们的成员变量是 static 的时候,那么它的生命周期将和整个app的生命周期一致。这必然会导致一系列问题,
不要在类初始时初始化静态成员。可以考虑 lazy初始化(延迟加载)
单例的静态特性,使得它的生命周期和应用的生命周期会一样长,所以一旦使用有误,小心无限制的持有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,推荐的使用 Application 的 Context。当然,Application 的 context 不是万能的,所以也不能随便乱用,对于有些地方则必须使用 Activity 的 Context,对于Application,Service,Activity三者的 Context 的应用场景如下: