前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Android内存泄露和ANR

Android内存泄露和ANR

原创
作者头像
进击的阿斌
发布2025-02-13 23:18:03
发布2025-02-13 23:18:03
1440
举报

1、什么是内存泄漏?在 Android 中如何检测和避免内存泄漏?

内存泄漏(Memory Leak)是指程序在运行过程中,由于疏忽或错误未能释放不再使用的内存,导致这部分内存无法被回收,最终可能引发应用卡顿、崩溃或系统性能下降。


Android 中常见的内存泄漏场景

1. 静态引用 Activity/View

静态变量(如单例)持有 Activity 或 View 的引用,导致 Activity 销毁时无法被回收。

2. 非静态内部类

非静态内部类(如 Handler、Runnable)隐式持有外部类(如 Activity)的引用,若其生命周期长于外部类,会导致泄漏。

3. 匿名内部类

匿名内部类(如回调、监听器)隐式持有外部类的引用。

4. 未注销监听器或回调

例如未在 onDestroy() 中移除 BroadcastReceiverLiveData 观察者等。

5. 资源未释放

文件流、数据库游标(Cursor)等未及时关闭。


检测内存泄漏的方法

1. 使用 Android Profiler
  • 打开 Android Studio 的 Profiler 工具。
  • 选择 Memory 分析器,观察内存分配情况。
  • 触发疑似泄漏的操作后,手动执行 GC(点击垃圾桶图标),若内存未下降则可能存在泄漏。
  • 生成 Heap Dump(堆转储文件),分析对象引用关系。
2. 使用 LeakCanary
  • 添加依赖:
代码语言:xml
复制
     dependencies {
       debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
     }
3. 手动分析堆转储
  • 使用 MAT(Memory Analyzer Tool) 或 Android Studio 的 Heap Analyzer 分析堆转储文件。
  • 查找重复实例、残留的 Activity 或 Fragment 对象,查看其 GC Root 引用路径。

避免内存泄漏的关键实践

1. 使用弱引用(WeakReference)
  • 在静态内部类中,通过 WeakReference 持有 Activity 的引用:
代码语言:java
复制
     private static class MyHandler extends Handler {
         private final WeakReference<Activity> activityRef;
         MyHandler(Activity activity) {
             activityRef = new WeakReference<>(activity);
         }
         @Override
         public void handleMessage(Message msg) {
             Activity activity = activityRef.get();
             if (activity != null) {
                 // 更新 UI
             }
         }
     }
2. 及时注销监听器和回调
代码语言:java
复制
   @Override
   protected void onDestroy() {
       super.onDestroy();
       // 移除 Handler 的消息
       handler.removeCallbacksAndMessages(null);
       // 注销广播接收器
       unregisterReceiver(broadcastReceiver);
       // 移除 LiveData 观察者
       liveData.removeObserver(observer);
   }
3. 避免静态变量持有 Context
  • 使用 Application Context 代替 Activity Context(例如单例模式中):
代码语言:java
复制
     public class MySingleton {
         private static MySingleton instance;
         private Context appContext;

         private MySingleton(Context context) {
             appContext = context.getApplicationContext();
         }

         public static MySingleton getInstance(Context context) {
             if (instance == null) {
                 instance = new MySingleton(context);
             }
             return instance;
         }
     }
4. 谨慎使用匿名内部类
  • 将匿名内部类改为静态内部类,或确保其生命周期不超过外部类。
5. 关闭资源
代码语言:java
复制
   try {
       InputStream is = new FileInputStream(...);
       // 使用资源
   } finally {
       is.close(); // 确保资源被释放
   }

总结

内存泄漏的核心问题是长生命周期对象持有短生命周期对象的引用。通过合理使用弱引用、及时释放资源、借助工具分析,可以显著减少泄漏风险。在 Android 开发中,养成主动管理对象生命周期的习惯至关重要。

2、什么是 ANR(Application Not Responding)?如何避免 ANR 的发生?

ANR(Application Not Responding)的定义

ANR 是 Android 系统中应用无响应的警告机制。当应用主线程(UI 线程)被长时间阻塞(如执行耗时操作),导致用户输入事件(点击、滑动等)或 BroadcastReceiver 无法在合理时间内处理,系统会弹出 ANR 弹窗,提示用户选择“等待”或“关闭应用”。

ANR 的触发条件

场景

超时阈值

原因

输入事件(如点击、滑动)未响应

5 秒

主线程被耗时操作(如网络请求、复杂计算)阻塞。

BroadcastReceiver.onReceive() 未完成

前台 10 秒 / 后台 60 秒

onReceive() 中执行耗时操作(如数据库读写)。

Service 生命周期方法未完成

前台 20 秒

ServiceonCreate()onStartCommand() 等主线程方法执行时间过长。

ANR 的常见原因

  1. 主线程执行耗时操作
    • 网络请求、数据库读写、大文件 IO、复杂计算等。
  2. 线程死锁或资源竞争
    • 多线程同步问题导致主线程等待。
  3. 过度复杂的布局或渲染
    • 嵌套过深的布局、频繁的 UI 重绘(如动画)。
  4. 低效的 BroadcastReceiverContentObserver
    • 未及时注销监听器,或在回调中执行耗时逻辑。

检测和定位 ANR

1. 日志分析
  • 当 ANR 发生时,系统会生成 /data/anr/traces.txt 文件,记录主线程的堆栈信息:adb pull /data/anr/traces.txt
  • 查找主线程中阻塞的方法调用(如 Thread.sleep()wait()、锁竞争等)。
2. 使用 Android Studio 工具
  • Android Profiler:监控主线程的 CPU 使用率和卡顿情况。
  • StrictMode:检测主线程的磁盘/网络操作:
代码语言:java
复制
  public class MyApp extends Application {
      @Override
      public void onCreate() {
          super.onCreate();
          StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                  .detectDiskReads()
                  .detectDiskWrites()
                  .detectNetwork()
                  .penaltyLog() // 输出日志警告
                  .build());
      }
  }
3. 第三方工具
  • Firebase Crashlytics:监控线上 ANR 发生率和堆栈。
  • BlockCanary:检测主线程卡顿。

避免 ANR 的关键实践

1. 将耗时操作移至子线程
  • 使用异步框架处理耗时任务:
    • Kotlin 协程
代码语言:kotlin
复制
    viewModelScope.launch(Dispatchers.IO) {
        // 执行网络请求或数据库操作
        val result = api.fetchData()
        withContext(Dispatchers.Main) {
            updateUI(result) // 返回主线程更新 UI
        }
    }
  • RxJava
代码语言:java
复制
    Observable.fromCallable(() -> doBackgroundWork())
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(result -> updateUI(result));
2. 优化主线程任务
  • 避免在 onCreate()onResume() 中执行复杂逻辑。
  • 使用 IdleHandler 处理低优先级任务:
代码语言:java
复制
  Looper.myQueue().addIdleHandler(() -> {
      // 在主线程序列空闲时执行
      return false; // true 表示保留 Handler,false 表示移除
  });
3. 合理使用 BroadcastReceiver
  • onReceive() 中仅处理轻量级逻辑,如需耗时操作,启动 IntentServiceWorkManager
代码语言:java
复制
  public class MyReceiver extends BroadcastReceiver {
      @Override
      public void onReceive(Context context, Intent intent) {
          // 启动 Service 处理耗时任务
          Intent serviceIntent = new Intent(context, MyService.class);
          context.startService(serviceIntent);
      }
  }
4. 优化 UI 渲染性能
  • 减少布局层级(使用 ConstraintLayout)。
  • 避免 ListView/RecyclerView 的过度绘制。
  • 使用 ViewStub 延迟加载复杂视图。
5. 避免内存泄漏
  • 内存泄漏会导致频繁 GC,间接引发主线程卡顿(见内存泄漏解决方案)。

ANR 处理的紧急方案

若线上发生 ANR,需快速定位问题:

  1. 通过 traces.txt 或 Firebase 获取堆栈信息。
  2. 复现问题:在相同设备/场景下触发 ANR。
  3. 使用 Systrace 分析主线程阻塞点:
代码语言:bash
复制
   python systrace.py -t 10 sched gfx view wm am app

总结

ANR 的核心矛盾是 主线程被阻塞导致用户交互无响应。通过以下原则可显著降低风险:

  • 异步化:所有耗时操作交给子线程。
  • 轻量化主线程:仅处理 UI 更新和轻量级逻辑。
  • 监控与优化:利用工具持续检测性能瓶颈。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、什么是内存泄漏?在 Android 中如何检测和避免内存泄漏?
    • Android 中常见的内存泄漏场景
    • 检测内存泄漏的方法
    • 避免内存泄漏的关键实践
    • 总结
  • 2、什么是 ANR(Application Not Responding)?如何避免 ANR 的发生?
    • ANR(Application Not Responding)的定义
    • ANR 的触发条件
    • ANR 的常见原因
    • 检测和定位 ANR
    • 避免 ANR 的关键实践
    • ANR 处理的紧急方案
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档