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

news2024/11/20 12:31:02

本文讲解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()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            // 处理消息
        }
    };

    @Override
    protected 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);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            MainActivity activity = activityReference.get();
            if (activity != null) {
                // 处理消息
            }
        }
    }

    private final MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler.postDelayed(() -> {
            // 延迟任务
        }, 10000);
    }
}

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

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

场景示例

public class MainActivity extends AppCompatActivity {
    private class MyTask extends AsyncTask {
        @Override
        protected Void doInBackground(Void... voids) {
            // 执行异步任务
            return null;
        }
    }

    @Override
    protected 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);
        }

        @Override
        protected Void doInBackground(Void... voids) {
            // 执行异步任务
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            MainActivity activity = activityReference.get();
            if (activity != null) {
                // 更新 UI
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new MyTask(this).execute();
    }
}

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

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

场景示例

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

            @Override
            public void onViewDetachedFromWindow(View v) {
            }
        });
    }
}

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

解决方案

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

修复代码

public class MainActivity extends AppCompatActivity {
    private View.OnAttachStateChangeListener listener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View view = findViewById(R.id.my_view);
        listener = new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {
            }

            @Override
            public void onViewDetachedFromWindow(View v) {
            }
        };
        view.addOnAttachStateChangeListener(listener);
    }

    @Override
    protected 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.coloradmin.cn/o/2244031.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

红外相机和RGB相机外参标定 - 无需标定板方案

1. 动机 在之前的文章中红外相机和RGB相机标定:实现两种模态数据融合_红外相机标定-CSDN博客 ,介绍了如何利用标定板实现外参标定;但实测下来发现2个问题: (1)红外标定板尺寸问题,由于标定板小…

即插即用篇 | YOLOv11 引入高效的直方图Transformer模块 | 突破天气障碍:Histoformer引领高效图像修复新路径

本改进已同步到YOLO-Magic框架! 摘要:摘要。基于Transformer的恶劣天气图像修复方法取得了显著进展。大多数方法通过沿通道维度或在空间上固定范围的块内使用自注意力,以减少计算负担。然而,这种折中方式在捕获长距离空间特征方面…

ITSS服务经理: 山西科技学院智能铸造现代产业学院揭牌

记者从山西科技学院传来喜讯,近日,在该院工程训练中心与智能铸造现代产业学院于山西省晋城市泽州县绿色智能铸造创新产业园隆重举行的揭牌启动仪式上,标志着学院迈入崭新篇章。应用型本科高校,作为孕育高素质应用人才的摇篮&#…

AI时代:弯道超车的新思维与实践路径

文章目录 一、AI时代的机遇与挑战二、重新认识AI三、弯道超车的新思维四、实践路径与案例分享五、AI技术的未来发展趋势六、个人与企业如何适应AI时代《AI时代:弯道超车新思维》内容简介作者简介目录 在科技日新月异的今天,人工智能(AI&#…

‘视’不可挡:OAK相机助力无人机智控飞行!

南京邮电大学通达学院的刘同学用我们的oak-d-lite实现精确打击无人机的避障和目标识别定位功能,取得了比赛冠军。我们盼望着更多的朋友们能够加入到我们OAK的队伍中来,参与到各式各样的比赛中去。我们相信,有了我们相机的助力,大家…

网页抓取API,让数据获取更简单

网页抓取的过程通常分为以下步骤,尤其是在面对静态网页时: 获取页面 HTML:使用 HTTP 客户端下载目标页面的 HTML 内容。解析 HTML:将下载的 HTML 输入解析器,准备提取内容。提取数据:利用解析器功能&#…

Java学习笔记--数组常见算法:数组翻转,冒泡排序,二分查找

一,数组翻转 1.概述:数组对称索引位置上的元素互换,最大值数组序号是数组长度减一 创建跳板temp,进行min和max的互换,然后min自增,max自减,当min>max的时候停止互换,代表到中间值 用代码实…

Office-Tab-for-Mac Office 窗口标签化,Office 多文件标签化管理

Office Tab:让操作更高效,给微软 Office 添加多标签页功能 Office 可以说是大家装机必备的软件,无论学习还是工作都少不了。其中最强大、用的最多的,还是微软的 Microsoft Office。 遗憾的是,微软的 Office 不支持多…

游戏引擎学习第12天

视频参考:https://www.bilibili.com/video/BV1yom9YnEWY 这节没讲什么东西,主要是改了一下音频的代码 后面有介绍一些alloc 和malloc,VirtualAlloc 的东西 _alloca 函数(或 alloca)分配的是栈内存,它的特点是: 生命周…

delphi fmx android 离线人脸识别

搜遍全网都没有找到delphi android 能用的 离线人脸识别,无需注册什么开发者 有这方面需求的可以用fsdk 这边用的luxand.FSDK8.0 android下的注册号要自己找下 1,用老猫的工具将android 下的sdk,FSDK.java 编译成FSDK.jar 老猫的工具 2,用上面的工具将FSDK.jar 生成de…

Java基础夯实——2.4 线程的生命周期

Java线程生命周期 Java线程的生命周期分为:新建(New)、就绪(Runnable)、阻塞(Blocked)、等待 (Waiting) 、计时等待(Timed_Waiting)、终止(Terminated&#…

实现简易计算器 网格布局 QT环境 纯代码C++实现

问题:通过代码完成一个10以内加减法计算器。不需要自适应,界面固定360*350。 ""按钮90*140,其它按钮90*70。 参考样式 #define DEFULT_BUTTON_STYLE "\ QPushButton{\color:#000000;\border:1px solid #AAAAAA;\border-radi…

RNN公式解释:实现记忆功能;RNN的状态向量

目录 RNN公式解释:实现记忆功能 一、词向量 二、RNN的状态向量 三、词向量变为状态向量的过程 四、总结 RNN公式解释:实现记忆功能 在RNN(递归神经网络)中,词向量变为状态向量的过程,实际上是RNN处理时序数据的一个核心环节。以下是对这一过程的详细解释: 一、词向…

【Linux】基础02

Linux编译和调试 VI编辑文件 vi : 进入文件编辑 是命令行模式 i :从光标处进入插入模式 dd : 删除光标所在行 n dd 删除指定行数 Esc : 退出插入模式 : 冒号进入末行模式 :wq : 保存退出 :q : 未修改文件可以退出 :q! …

21.UE5游戏存档,读档,函数库

2-23 游戏存档、读档、函数库_哔哩哔哩_bilibili 目录 1.存档蓝图 2.函数库 2.1保存存档 2.2读取存档: 3.加载游戏,保存游戏 3.1游戏实例对象 3.2 加载游戏 3.3保存游戏 这一节的内容较为错综复杂,中间没有运行程序进行阶段性成果的验…

未来已来:少儿编程竞赛聚焦物联网,激发创新潜力

随着人工智能与物联网技术(IoT)的快速发展,少儿编程教育正在迎来新的变革浪潮。近年来,各类少儿编程竞赛纷纷增加了物联网相关主题,要求学生结合编程知识和硬件设备设计智能家居、智慧城市等创新项目。这一趋势不仅丰富…

布局设计器

介绍 最近遇到一个设计器的需求,要求拖拽布局,图层管理,自定义组件预览,分辨率等等功能。说白了就是先用设计器布局然后在屏幕上播放你布局好的内容 所以不多说了直接上代码 代码地址 这里大概说下有哪些功能吧 图层与属性框的值关…

Java中日志采集框架-JUL、Slf4j、Log4j、Logstash

1. 日志采集 日志采集是指在软件系统、网络设备、服务器或其他IT基础设施中自动收集日志文件和事件信息的过程。这些日志通常包含了时间戳、事件类型、源和目标信息、错误代码、用户操作记录等关键数据。日志采集的目的是为了监控系统运行状态、分析系统性能、审计用户行为、故…

ansible从入门到精通(完整篇)

ansible从入门到精通(完整篇) 转自ansible从入门到精通(完整篇) 文章目录 01 Ansible介绍与安装 1. 介绍 Ansible 1.1 什么是 Ansible?1.2 Ansible 无需代理1.3 Ansible 方式 2. 安装 Ansible 2.1 控制节点2.2 受管主机2.3…

Python自学之Colormaps指南

目录 1.色彩映射表(Colormaps)是什么? 2.Matplotlib中的色彩映射表类型 2.1同色渐变(Sequential Colormaps) 2.2双色渐变(Divergence Colormaps) 2.3定性色彩(Qualitative Col…