Android开发——避免内存泄露

简介:

Android开发——避免内存泄露

本文翻译自Avoiding memory leak——Post by Romain Guy
著作权归原作者所有。转载请注明出处,由JohnTsai翻译


Android应用被分配的堆的大小限制为16MB。这对于手机来说已经很多了,但对于一些开发者想获得的来说仍旧不够。即使你没有计划使用所有的这些内存。你应该尽可能的少用以避免其他应用在运行时因为内存不足而被杀掉。Android内存中保存的应用越多,用户在应用间切换得越快。作为我工作的一部分,我在Android应用中遇到过得内存泄露问题,它们大多数时候是因为同一个错误:对Context维持了一个长时间的引用


在Android中,Context可用于很多操作,但主要是用于加载和访问资源。这就是为什么所有的widget都要在它们的构造方法中接收Context参数。在一个常规的Android应用中,通常有两种Context:ActivityApplication

一般开发者将前者传递给需要Context的类和方法:

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  setContentView(label);
}

这意味着View有一整个Activity的引用。因此可访问到Activity持有的任何东西。因此,如果你泄露了Context(泄露(leak)意味着你维持了一个指向它的引用导致GC不能回收它),你就泄露了很多内存。如果你不小心的话,非常容易就会泄露整个Activity。

当屏幕的方向改变时,系统默认会销毁当前的Activity并保存它的状态,然后创建一个新的Activity。在这个过程中,Android会从资源中重新加载应用的UI。现在假设你写了一个有大bitmap的应用,你不想每次旋转时都加载bitmap。维持这个实例、不需要每次重新加载的最简单的办法就是将它维持在一个静态域中。

private static Drawable sBackground;

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);
  
  setContentView(label);
}

这个代码是非常便捷但是也非常错误。它泄露了第一次因屏幕方向改变而创建的Activity。当一个Drawable对象依附到一个View对象上时,View对象被作为一个callback设置到drawable对象上了。在上面的代码片段中,这就意味着drawable有一个TextView的引用,而TextView有activity的引用(Context),activity有很多东西的原因(取决于你的代码)

这个例子是Context泄露的最简单的例子,你可以看看我们在Home screen的源码中是如何处理这种问题的(看unbindDrawables()方法),通过在activity销毁时,将存储的drawable的callback为null。有趣的是,有多种情况能导致我们创建泄露的Context,这样很糟糕。它们使得我们很快就耗尽内存。

有两种简单的方法可避免Context相关的内存泄露。

  • 最明显的方法是避免在Context的作用域之外使用它。上面那个例子展示了静态引用或是内部类对外部类的隐式引用都是同样危险的。
  • 第二种方法就是使用Application Context。这个Context会一直存活只要你的应用是活着的,并且不依赖于Activity的生命周期。如果你打算维持一个长时间存在的并且需要Context的对象时,记住使用应用的Context。获取方法:Context.getApplicationContext()或Activity.getApplication()

概况来说,为了避免Context相关的内存泄露,记住下面几点:

  • 不要维持一个长时间存在对Activity的Context的引用(Activity的引用和Activity有着一样的生命周期)
  • 使用Application的Context而不是Activity的Context
  • 避免在Activity中使用非静态内部类,如果你不想控制他们的生命周期。使用静态内部类,并在它的内部创建一个对Activity的弱引用。

下面的例子由译者补充

使用非静态内部类,Android Studio报可能内存泄露的警告:
QQ20160308-1%402x.png

//解决方法
//使用静态内部类,并在其中创建对Activity的弱引用
 private static class MyHandler extends Handler{

        //对Activity的弱引用
        private final WeakReference<HandlerActivity> mActivity;

        public MyHandler(HandlerActivity activity){
            mActivity = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity = mActivity.get();
            if(activity==null){
                super.handleMessage(msg);
                return;
            }
            switch (msg.what) {
                case DOWNLOAD_FAILED:
                    Toast.makeText(activity, "下载失败", Toast.LENGTH_SHORT).show();
                    break;
                case DOWNLOAD_SUCCESS:
                    Toast.makeText(activity, "下载成功", Toast.LENGTH_SHORT).show();
                    Bitmap bitmap = (Bitmap) msg.obj;
                    activity.imageView.setVisibility(View.VISIBLE);
                    activity.imageView.setImageBitmap(bitmap);
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    }

    private final MyHandler mHandler = new MyHandler(this);
  • 垃圾回收器不是针对内存泄露的保险。

如果本文对你的开发有所帮助,并且你手头恰好有零钱。

不如打赏我一杯咖啡,鼓励我继续分享优秀的文章。



原文地址:http://www.cnblogs.com/JohnTsai/p/5256295.html
相关文章
|
2天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
22 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
25天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
5天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
5 0
|
7天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
16天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
18天前
|
监控 算法 Android开发
安卓应用开发:打造高效启动流程
【4月更文挑战第5天】 在移动应用的世界中,用户的第一印象至关重要。特别是对于安卓应用而言,启动时间是用户体验的关键指标之一。本文将深入探讨如何优化安卓应用的启动流程,从而减少启动时间,提升用户满意度。我们将从分析应用启动流程的各个阶段入手,提出一系列实用的技术策略,包括代码层面的优化、资源加载的管理以及异步初始化等,帮助开发者构建快速响应的安卓应用。
|
18天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
19 1
Android开发之使用OpenGL实现翻书动画
|
18天前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
15 1
Android开发之OpenGL的画笔工具GL10
|
25天前
|
Android开发
Android开发小技巧:怎样在 textview 前面加上一个小图标。
Android开发小技巧:怎样在 textview 前面加上一个小图标。
12 0
|
25天前
|
Android开发
Android 开发 pickerview 自定义选择器
Android 开发 pickerview 自定义选择器
12 0