Android中常见内存泄漏的场景和解决方案

embedded/2024/11/21 19:51:03/

本文讲解Android 开发中常见内存泄漏场景及其解决方案,内容包括代码示例、原因分析以及最佳实践建议。

在这里插入图片描述

在这里插入图片描述

1. 静态变量导致的内存泄漏

静态变量的生命周期与应用进程一致,如果静态变量持有了对 Activity 或其他大对象的引用,就可能导致内存泄漏

场景示例

public class MemoryLeakExample {// 静态变量持有 Activity 的引用private static Context sContext;public static void setContext(Context context) {sContext = context;}
}

如果在 onCreate() 方法中调用了 MemoryLeakExample.setContext(this),即使 Activity 销毁,sContext 仍然持有对 Activity 的引用,导致内存泄漏

解决方案

  • 避免使用静态变量持有对 Context 的引用。
  • 使用 ApplicationContext 替代 Activity 的 Context。

修复代码

public class MemoryLeakExample {private static Context sContext;public static void setContext(Context context) {// 使用 ApplicationContext 避免泄漏sContext = context.getApplicationContext();}
}

2. Handler 导致的内存泄漏

Handler 会隐式持有外部类的引用,导致外部类无法被垃圾回收。

场景示例

public class MainActivity extends AppCompatActivity {private final Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {// 处理消息}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);handler.postDelayed(() -> {// 延迟任务}, 10000);}
}

如果在任务执行前 Activity 被销毁,Handler 仍然持有对 Activity 的引用。

解决方案

  • 将 Handler 定义为静态内部类,避免隐式引用外部类。
  • 使用弱引用(WeakReference)来引用外部类。

修复代码

public class MainActivity extends AppCompatActivity {private static class MyHandler extends Handler {private final WeakReference activityReference;public MyHandler(MainActivity activity) {super(Looper.getMainLooper());activityReference = new WeakReference<>(activity);}@Overridepublic void handleMessage(@NonNull Message msg) {MainActivity activity = activityReference.get();if (activity != null) {// 处理消息}}}private final MyHandler handler = new MyHandler(this);@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);handler.postDelayed(() -> {// 延迟任务}, 10000);}
}

3. 非静态内部类持有外部类的引用

非静态内部类会隐式持有其外部类的引用,如果长时间持有,则可能导致内存泄漏

场景示例

public class MainActivity extends AppCompatActivity {private class MyTask extends AsyncTask {@Overrideprotected Void doInBackground(Void... voids) {// 执行异步任务return null;}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);new MyTask().execute();}
}

如果 MyTask 执行时间较长,而 Activity 已销毁,则会导致泄漏。

解决方案

  • 将内部类声明为静态。
  • 使用弱引用(WeakReference)访问外部类实例。

修复代码

public class MainActivity extends AppCompatActivity {private static class MyTask extends AsyncTask {private final WeakReference activityReference;MyTask(MainActivity activity) {activityReference = new WeakReference<>(activity);}@Overrideprotected Void doInBackground(Void... voids) {// 执行异步任务return null;}@Overrideprotected void onPostExecute(Void aVoid) {MainActivity activity = activityReference.get();if (activity != null) {// 更新 UI}}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);new MyTask(this).execute();}
}

4. 监听器或回调未正确移除

监听器或回调注册后,如果不及时移除,会导致对象无法释放。

场景示例

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);View view = findViewById(R.id.my_view);view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {@Overridepublic void onViewAttachedToWindow(View v) {}@Overridepublic void onViewDetachedFromWindow(View v) {}});}
}

如果未移除 OnAttachStateChangeListenerMainActivity 的引用可能被保留。

解决方案

在适当的生命周期方法中移除监听器或回调。

修复代码

public class MainActivity extends AppCompatActivity {private View.OnAttachStateChangeListener listener;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);View view = findViewById(R.id.my_view);listener = new View.OnAttachStateChangeListener() {@Overridepublic void onViewAttachedToWindow(View v) {}@Overridepublic void onViewDetachedFromWindow(View v) {}};view.addOnAttachStateChangeListener(listener);}@Overrideprotected void onDestroy() {super.onDestroy();View view = findViewById(R.id.my_view);if (view != null && listener != null) {view.removeOnAttachStateChangeListener(listener);}}
}

5. 单例模式导致的内存泄漏

单例对象的生命周期与应用一致,如果单例持有了对 Context 或 Activity 的引用,就会导致泄漏。

场景示例

public class Singleton {private static Singleton instance;private Context context;private Singleton(Context context) {this.context = context;}public static Singleton getInstance(Context context) {if (instance == null) {instance = new Singleton(context);}return instance;}
}

在获取 Singleton 时传入了 ActivityContext,会导致泄漏。

解决方案

  • 使用 ApplicationContext。
  • 避免单例直接持有 Context。

修复代码

public class Singleton {private static Singleton instance;private Context context;private Singleton(Context context) {// 使用 ApplicationContext 避免泄漏this.context = context.getApplicationContext();}public static Singleton getInstance(Context context) {if (instance == null) {instance = new Singleton(context);}return instance;}
}

6. 其他常见场景

6.1 Bitmap 未及时回收

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large_image);
// 使用完毕后应回收
bitmap.recycle();

6.2 WebView 泄漏

WebView webView = new WebView(context);
webView.destroy();

以上示例涵盖了 Android 中常见的内存泄漏场景及其解决方法,通过合理使用静态类、弱引用以及生命周期管理,可以有效减少内存泄漏问题。


http://www.ppmy.cn/embedded/139413.html

相关文章

基于yolov8、yolov5的电塔缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要:电塔缺陷检测在电力设备巡检、运行维护和故障预防中起着至关重要的作用,不仅能帮助相关部门实时监测电塔运行状态,还为智能化检测系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的电塔缺陷检测模型&#xf…

昇思MindSpore第四课---GPT实现情感分类

1. GPT的概念 GPT 系列是 OpenAI 的一系列预训练模型,GPT 的全称是 Generative Pre-Trained Transformer,顾名思义,GPT 的目标是通过Transformer,使用预训练技术得到通用的语言模型。和BERT类似,GPT-1同样采取pre-trai…

【WPF】Prism学习(六)

Prism Dependency Injection 1.依赖注入(Dependency Injection) 1.1. Prism与依赖注入的关系: Prism框架一直围绕依赖注入构建,这有助于构建可维护和可测试的应用程序,并减少或消除对静态和循环引用的依赖。 1.2. P…

【CVPR2024】2024年CVPR的3D 目标检测的综述(还在补充中)

透视单目DETR 2D 透视单目DETR 2D的。对于DETR系列消除了 NMS的2D目标检测,RT-DETR[8]设计了一个高效的混合编码器,通过解耦尺度内交互和跨尺度融合来提高速度。采用不确定性最小查询选择,为解码器提供高质量的初始查询,从而提高准确率。 透视单目3D 透视单目3D。MonoCD…

【HOT100第五天】搜索二维矩阵 II,相交链表,反转链表,回文链表

240.搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 先动手写写最简单方法,二重循环。 class Solution { public:bool searchMa…

【大数据学习 | Spark】详解分区个数

RDD默认带有分区的,那么创建完毕rdd以后他的分区数量是多少? 从hdfs读取文件的方式是最正规的方式,我们通过计算原理可以推出blk的个数和分区数量是一致的,本地化计算。 我们可以发现数据的读取使用的是textInputFormat&#xff…

【C#】C#编程基础:探索控制台应用与数据操作

文章目录 前言:1. 控制台项目的使用2. 数值隐式类型转换与强制类型转换2.1 隐式类型转换2.2 数值强制转换2.3 字符串强制转换2.4 万能转换器 3. 字符串与数组3.1 字符串处理方法3.2 数组的创建与遍历3.3 字符串与数组之间的使用方法 4. 引用类型与值类型5. 常量cons…

实验室管理自动化:Spring Boot技术的应用

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理实验室管理系统的相关信息成为必然。开发合…