Android中的内存泄漏总结

在Android开发中,内存泄漏(Memory Leak)指的是对象无法被垃圾回收器(GC)回收,导致内存持续占用,最终可能引发OOM(OutOfMemoryError)。以下是常见的内存泄漏类型及对应的解决方案:

一、常见内存泄漏类型

1. 静态变量持有Activity/Fragment引用
  • 原因:静态变量生命周期与应用进程一致,若持有Activity引用会导致Activity无法被回收。
  • 示例
    public class MainActivity extends Activity {
        private static Context context; // 静态变量持有Activity引用
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            context = this; // 内存泄漏
        }
    }
    
  • 解决方案
    • 使用Application Context替代Activity Context。
    • 避免在静态变量中存储Activity/Fragment的非静态内部类。
2. 非静态内部类/匿名类持有外部类引用
  • 原因:非静态内部类(如Handler、Runnable、AsyncTask)会隐式持有外部Activity的引用,若生命周期长于Activity(如异步任务未完成),会导致Activity无法回收。
  • 示例
    public class MainActivity extends Activity {
        private Handler handler = new Handler() { // 非静态内部类
            @Override
            public void handleMessage(Message msg) {
                // 更新UI
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            handler.postDelayed(() -> { /* 长时间运行的任务 */ }, 10000);
        }
    }
    
  • 解决方案
    • 使用静态内部类 + 弱引用(WeakReference)
      private static class MyHandler extends Handler {
          private final WeakReference<MainActivity> activityRef;
          public MyHandler(MainActivity activity) {
              this.activityRef = new WeakReference<>(activity);
          }
          @Override
          public void handleMessage(Message msg) {
              MainActivity activity = activityRef.get();
              if (activity != null) {
                  // 更新UI
              }
          }
      }
      
    • 在Activity销毁时移除所有未处理的消息:
      @Override
      protected void onDestroy() {
          super.onDestroy();
          handler.removeCallbacksAndMessages(null);
      }
      
3. 资源未关闭导致泄漏
  • 原因:未正确关闭Cursor、File、InputStream/OutputStream、SQLiteDatabase等资源,导致GC无法回收。
  • 示例
    public void readFile() {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("file.txt");
            // 读取文件
        } catch (IOException e) {
            e.printStackTrace();
        } 
        // 未关闭fis,导致泄漏
    }
    
  • 解决方案
    • 使用try-with-resources语句(Java 7+):
      public void readFile() {
          try (FileInputStream fis = new FileInputStream("file.txt")) {
              // 自动关闭资源
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      
    • finally块中手动关闭资源:
      finally {
          if (fis != null) {
              try {
                  fis.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      
4. 注册/监听未注销导致泄漏
  • 原因:注册广播接收器(BroadcastReceiver)、传感器监听(SensorListener)、数据库监听(ContentObserver)等后,未在Activity销毁时注销。
  • 示例
    public class MainActivity extends Activity {
        private BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // 处理广播
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            registerReceiver(receiver, new IntentFilter("ACTION")); // 注册
        }
    
        // 未在onDestroy()中调用unregisterReceiver(receiver)
    }
    
  • 解决方案
    • onDestroy()中注销监听:
      @Override
      protected void onDestroy() {
          super.onDestroy();
          unregisterReceiver(receiver);
      }
      
5. 动画未停止导致泄漏
  • 原因:属性动画(ObjectAnimator)或无限循环动画未在Activity销毁时停止,导致View无法被回收。
  • 示例
    public class MainActivity extends Activity {
        private ObjectAnimator animator;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            animator = ObjectAnimator.ofFloat(view, "alpha", 0, 1);
            animator.setDuration(1000);
            animator.setRepeatCount(ValueAnimator.INFINITE);
            animator.start();
        }
    
        // 未在onDestroy()中停止动画
    }
    
  • 解决方案
    • onDestroy()中停止动画:
      @Override
      protected void onDestroy() {
          super.onDestroy();
          if (animator != null && animator.isRunning()) {
              animator.cancel();
          }
      }
      
6. 单例持有Activity引用
  • 原因:单例模式的生命周期是全局的,若持有Activity引用会导致Activity无法回收。
  • 示例
    public class Singleton {
        private static Singleton instance;
        private Context context;
    
        private Singleton(Context context) {
            this.context = context; // 传入Activity Context导致泄漏
        }
    
        public static Singleton getInstance(Context context) {
            if (instance == null) {
                instance = new Singleton(context);
            }
            return instance;
        }
    }
    
  • 解决方案
    • 使用Application Context
      public static Singleton getInstance(Context context) {
          if (instance == null) {
              instance = new Singleton(context.getApplicationContext()); // 使用App Context
          }
          return instance;
      }
      
7. WebView内存泄漏
  • 原因:WebView持有Activity引用,且其内部线程生命周期长于Activity,导致Activity无法回收。
  • 解决方案
    • 单独创建进程加载WebView,通过AIDL通信。
    • onDestroy()中销毁WebView:
      @Override
      protected void onDestroy() {
          super.onDestroy();
          if (webView != null) {
              webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
              webView.clearHistory();
              ((ViewGroup) webView.getParent()).removeView(webView);
              webView.destroy();
              webView = null;
          }
      }
      

二、内存泄漏检测工具

  1. Android Profiler:查看内存分配和对象存活情况。
  2. LeakCanary:自动检测内存泄漏并生成报告。
  3. MAT(Memory Analyzer Tool):分析堆转储文件(Heap Dump),定位大对象和引用链。
  4. Lint:静态代码分析工具,检查潜在的内存泄漏。

三、最佳实践总结

  1. 避免Activity/Fragment被长生命周期对象持有

    • 使用静态内部类 + 弱引用。
    • 使用Application Context替代Activity Context。
  2. 及时释放资源

    • 使用try-with-resources关闭流和数据库。
    • onDestroy()中注销广播接收器、停止动画、取消异步任务。
  3. 谨慎使用单例和静态变量

    • 确保单例只持有Application Context
    • 避免静态集合存储Activity/Fragment。
  4. 使用弱引用(WeakReference)

    • 适用于缓存、监听器等场景。
  5. 使用生命周期感知组件

    • 如ViewModel、LiveData自动管理生命周期。
  6. 定期进行内存分析

    • 使用工具检测和修复泄漏,特别是在性能敏感的应用中。

通过以上方法,可以有效减少Android应用中的内存泄漏,提高应用稳定性和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值