避免Android内存泄露

简介:

Android的应用被限制为最多占用16m的内存,至少在T-Mobile G1上是这样的(当然现在已经有几百兆的内存可以用了——译者注)。它包括电话本身占用的和开发者可以使用的两部分。即使你没有占用全部内存的打算,你也应该尽量少的使用内存,以免别的应用在运行的时候关闭你的应用。Android能在内存中保持的应用越多,用户在切换应用的时候就越快。作为我的一项工作,我仔细研究了Android应用的内存泄露问题,大多数情况下它们是由同一个错误引起的,那就是对一个上下文(Context)保持了长时间的引用。

在Android中,上下文(Context)被用作很多操作中,但是大部分是载入和访问资源。这就是所有的widget都会在它们的构造函数中接受一个上下文(Context)参数。在一个合格的Android应用中,你通常能够用到两种上下文(Context):活动(Activity)和应用(Application)。活动(Activity)通常被传递给需要上下文(Context)参数的类或者方法:

 
  1. <ol><li class="li1 odd"><div class="de1">@Override</div></li> 
  2. <li class="li1 even"><div class="de1"><span class="kw1">protected</span> <span class="kw4">void</span> onCreate<span class="br0">(</span>Bundle state<span class="br0">)</span> <span class="br0">{</span></div></li> 
  3. <li class="li1 odd"><div class="de1">  <span class="kw1">super</span>.<span class="me1">onCreate</span><span class="br0">(</span>state<span class="br0">)</span><span class="sy0">;</span></div></li> 
  4. <li class="li1 even"><div class="de1"> </div></li> 
  5. <li class="li1 odd"><div class="de1">  TextView label <span class="sy0">=</span> <span class="kw1">new</span> TextView<span class="br0">(</span><span class="kw1">this</span><span class="br0">)</span><span class="sy0">;</span></div></li> 
  6. <li class="li1 even"><div class="de1">  label.<span class="me1">setText</span><span class="br0">(</span><span class="st0">"Leaks are bad"</span><span class="br0">)</span><span class="sy0">;</span></div></li> 
  7. <li class="li1 odd"><div class="de1"> </div></li> 
  8. <li class="li1 even"><div class="de1">  setContentView<span class="br0">(</span>label<span class="br0">)</span><span class="sy0">;</span></div></li> 
  9. <li class="li1 odd"><div class="de1"><span class="br0">}</span></div></li></ol> 

这就意味着那个View有一个对整个活动(Activity)的引用并且对这个活动(Activity)中保持的所有对象有保持了引用;通常它们包括整个View的层次和它的所有资源。因此,如果你“泄露”了上下文(Context)(这里“泄露”的意思是你保持了一个引用并且组织GC收集它),你将造成大量的内存泄露。如果你不够小心的话,“泄露”一整个活动(Activity)是件非常简单的事情。

当屏幕的方向改变时系统会默认的销毁当前的活动(Activity)并且创建一个新的并且保持了它的状态。这样的结果就是Android会从资源中重新载入应用的UI。现在想象一下,你写了一个应用,有一个非常大的位图,并且你并不想在每次旋转时都重新载入。保留它并且每次旋转不重新加载的最简单的办法就是把它保存在一个静态字段上:

 
  1. <ol><li class="li1 odd"><div class="de1"><span class="kw1">private</span> <span class="kw1">static</span> Drawable sBackground<span class="sy0">;</span></div></li> 
  2. <li class="li1 even"><div class="de1"> </div></li> 
  3. <li class="li1 odd"><div class="de1">@Override</div></li> 
  4. <li class="li1 even"><div class="de1"><span class="kw1">protected</span> <span class="kw4">void</span> onCreate<span class="br0">(</span>Bundle state<span class="br0">)</span> <span class="br0">{</span></div></li> 
  5. <li class="li1 odd"><div class="de1">  <span class="kw1">super</span>.<span class="me1">onCreate</span><span class="br0">(</span>state<span class="br0">)</span><span class="sy0">;</span></div></li> 
  6. <li class="li1 even"><div class="de1"> </div></li> 
  7. <li class="li1 odd"><div class="de1">  TextView label <span class="sy0">=</span> <span class="kw1">new</span> TextView<span class="br0">(</span><span class="kw1">this</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1">  label.<span class="me1">setText</span><span class="br0">(</span><span class="st0">"Leaks are bad"</span><span class="br0">)</span><span class="sy0">;</span></div></li> 
  8. <li class="li1 odd"><div class="de1"> </div></li> 
  9. <li class="li1 even"><div class="de1">  <span class="kw1">if</span> <span class="br0">(</span>sBackground <span class="sy0">==</span> <span class="kw2">null</span><span class="br0">)</span> <span class="br0">{</span></div></li> 
  10. <li class="li1 odd"><div class="de1">    sBackground <span class="sy0">=</span> getDrawable<span class="br0">(</span>R.<span class="me1">drawable</span>.<span class="me1">large_bitmap</span><span class="br0">)</span><span class="sy0">;</span></div></li> 
  11. <li class="li1 even"><div class="de1">  <span class="br0">}</span></div></li><li class="li1 odd"><div class="de1">  label.<span class="me1">setBackgroundDrawable</span><span class="br0">(</span>sBackground<span class="br0">)</span><span class="sy0">;</span></div></li> 
  12. <li class="li1 even"><div class="de1"> </div></li> 
  13. <li class="li1 odd"><div class="de1">  setContentView<span class="br0">(</span>label<span class="br0">)</span><span class="sy0">;</span></div></li> 
  14. <li class="li1 even"><div class="de1"><span class="br0">}</span></div></li></ol> 

这段代码非常快,同时也错的够离谱。它泄露了当第一次屏幕角度改变时创建的第一个活动(Activity)。当一个Drawable被附加到一个View,这个View被设置为drawable的一个回调。在上面的代码片断中,这意味着这个Drawable对TextView有一个引用,同时这个TextView对Activity(Context对象)保持着引用,同时这个Activity对很多对象又有引用(这个多少还要看你的代码了)。

这个例子是造成Context泄露的最简单的一个原因,你可以看一下我们在主屏幕源码(查看unbindDrawables()方法)中是通过在Activity销毁时设置保存过的Drawable的回调为空来解决这个问题的。更为有趣的是,你可以创建一个context泄露的链,当然这非常的糟糕。它们可以让你飞快的用光所有的内存。

有两种简单的方法可以避免与context相关的内存泄露。最明显的一个就是避免在context的自身的范围外使用它。上面的例子展示了在类内部的一个静态的引用和它们对外部类的间接引用是非常危险的。第二个解决方案就是使用Application Context。这个context会伴随你的应用而存在,并且不依赖Activity的的生命周期。如果你计划保持一个需要context的长生命周期的对象,请记得考虑Application对象。你可以非常方便的通过调用Context.getApplicationContext() 或者 Activity.getApplication()获取它。

总之,为了避免涉及到context的内存泄露,请记住如下几点:

  1. 不要对一个Activity Context保持长生命周期的引用(一个对Activity的引用应该与Activity自身的生命周期相同)
  2. 尝试使用应用上下文(context-application)代替活动上下文(context-activity)
  3. 如果你不能控制它们的生命周期,在活动(Activity)中避免使用不是静态的内部类,使用静态类并且使用弱引用到活动(Activity)的内部。对于这个问题的解决方法是使用静态的内部类与一个弱引用(WeakReference)的外部类。就像ViewRoot和它的W内部类那么实现的。
  4. 垃圾回收器对于内存泄露来说并不是百分百保险的。

原文地址:Avoiding memory leaks



     本文转自xyz_lmn51CTO博客,原文链接:http://blog.51cto.com/xyzlmn/817115,如需转载请自行联系原作者

相关文章
|
1月前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
7月前
|
缓存 Java Shell
Android 内存泄露,怎样查找,怎么产生的内存泄露?
Android 内存泄露,怎样查找,怎么产生的内存泄露?
52 0
|
8天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
13 0
|
10天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
1月前
|
缓存 移动开发 Java
构建高效Android应用:内存优化实战指南
在移动开发领域,性能优化是提升用户体验的关键因素之一。特别是对于Android应用而言,由于设备和版本的多样性,内存管理成为开发者面临的一大挑战。本文将深入探讨Android内存优化的策略和技术,包括内存泄漏的诊断与解决、合理的数据结构选择、以及有效的资源释放机制。通过实际案例分析,我们旨在为开发者提供一套实用的内存优化工具和方法,以构建更加流畅和高效的Android应用。
|
1月前
|
监控 Java Android开发
构建高效Android应用:从内存管理到性能优化
【2月更文挑战第30天】 在移动开发领域,打造一个流畅且响应迅速的Android应用是每个开发者追求的目标。本文将深入探讨如何通过有效的内存管理和细致的性能调优来提升应用效率。我们将从分析内存泄露的根本原因出发,讨论垃圾回收机制,并探索多种内存优化策略。接着,文中将介绍多线程编程的最佳实践和UI渲染的关键技巧。最后,我们将通过一系列实用的性能测试工具和方法,帮助开发者监控、定位并解决性能瓶颈。这些技术的综合运用,将指导读者构建出更快速、更稳定、用户体验更佳的Android应用。
|
1月前
|
缓存 监控 API
构建高效的Android应用:从内存优化到电池寿命
【2月更文挑战第27天】 在移动开发领域,构建一个既高效又省电的Android应用是每个开发者的梦想。本文深入探讨了Android应用性能优化的关键策略,包括内存管理和电池使用效率。我们将分析常见的内存泄漏问题,并提供解决方案,同时介绍最新的Android电池优化技术。通过实例和最佳实践,读者将学会如何打造一个更加流畅、响应迅速且电池友好的Android应用。
|
1月前
|
传感器 缓存 Android开发
构建高效的Android应用:从内存优化到电池寿命
【2月更文挑战第23天】在移动开发领域,性能优化是一个持续的挑战。特别是对于Android应用来说,由于设备多样性和碎片化问题,开发者需要采取一系列策略来保证应用的流畅运行。本文深入探讨了Android应用的性能优化,包括内存管理、电池使用效率和UI渲染。我们将提供实用的技巧和最佳实践,帮助开发者构建更加高效、响应迅速的应用,从而改善用户体验并延长设备电池寿命。
14 1
|
1月前
|
监控 Java Android开发
构建高效Android应用:从内存优化到电池寿命
【2月更文挑战第18天】在移动设备的生态系统中,资源管理是确保用户满意度的关键。特别是对于Android开发者来说,优化应用的内存使用和延长电池寿命是提升用户体验的重要方面。本文将深入探讨Android平台上的内存管理机制,提供实用的代码级优化技巧,并讨论如何通过调整应用行为来减少能量消耗,最终目标是为开发者提供一套全面的技术策略,以打造更加高效、响应迅速且电池友好的Android应用。
32 5
|
7月前
|
算法 Java Android开发
Android rxjava和LiveData中的内存泄漏
Android rxjava和LiveData中的内存泄漏
118 0