关闭

避免内存泄露

639人阅读 评论(0) 收藏 举报

Android应用程序堆最大为16MB,至少在G1之前是这样(博主补充:Droid为24MB,Nexus One为32MB,Xoom为48MB)。对于手机而言已经是很大的内存空间了,但对于部分开发者而言还是很少的。即便没有将这些内存用完的打算,开发者也应尽量减少内存开销以便其他应用能够在后台运行而不会被强制关闭。这样的话,Android在内存中保存的应用越多,用户在应用间的切换就越快。我工作的一部分就是深入探索Android应用程序的内存泄露问题,大部分时间里,这些问题都是源自同一个错误:对Context(上下文环境)的长时间引用。

        在Android上,Context用于多种操作,但最多的还是用来加载和访问资源。这也是为什么所有的Widges在其构造函数中都有一个Context参数。常规的Android应用中,有两类Context:Activity Context和Application Context,通常前者被开发者传递给需要Context的类和方法。

[java] view plaincopy
  1. @Override  
  2. protected void onCreate(Bundle state) {  
  3.   super.onCreate(state);  
  4.     
  5.   TextView label = new TextView(this);  
  6.   label.setText("Leaks are bad");  
  7.     
  8.   setContentView(label);  
  9. }  
这就意味着那些视图引用了整个Activity及其所拥有的一切:一般是整个视图层和所有资源。因此,如果泄露了这类Context(这里的泄露指的是引用Context,从而阻止了GC(垃圾回收)操作),就泄露了很多内存空间。如果不小心,泄露整个Activity是非常容易的事。

        在进行屏幕方向改变的时候,系统默认做法是保持状态不变的情况下,销毁当前Activity并重新创建一个新的Activity。这样做,Android会从资源文件中重新装载当前应用的UI。现在假设你写了一个带有很大一幅位图的应用,但你不想在每次屏幕旋转时都装载一次位图,最简单的做法就是将其保存在一个静态区域中:

[java] view plaincopy
  1. private static Drawable sBackground;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle state) {  
  5.   super.onCreate(state);  
  6.     
  7.   TextView label = new TextView(this);  
  8.   label.setText("Leaks are bad");  
  9.     
  10.   if (sBackground == null) {  
  11.     sBackground = getDrawable(R.drawable.large_bitmap);  
  12.   }  
  13.   label.setBackgroundDrawable(sBackground);  
  14.     
  15.   setContentView(label);  
  16. }  
这段代码执行的快,同时也很有问题:在进行第一次屏幕方向改变的时候泄露了第一个Activity所占的内存空间。当一个Drawable连接到一个视图上时,视图被设置为Drawable上的一个回调,在上面的代码片段中,这就意味着Drawable引用了TextView,而TextView又引用了Activity Context,Activity Context又进一步引用了更多的东西(依赖与你的代码)。

        上面这段示例是最简单的泄露Activity Context的情况,你可以到Home Screen's Source Code查看unbindDrawables()方法中看看我们是如何通过在Acitivity销毁时将存储Drawable的回调置为null来解决该问题的。如果再有兴趣的话,某些情况下会产生一个由泄露的Context形成的链,这很糟糕,会很快使得内存耗尽。

        有两种方法可以避免Activity Context相关的内存泄露:最明显的一种是避免在Activity Context自身的范围之外对其进行引用。上面这段示例展示了静态引用的情况,但对内部类和外部类的隐式引用同样都是危险的。第二种解决方法是用Application Context,因为该Context与应用的生命周期一样长,并不依赖Activity的生命周期。如果想拥有一个生命期足够长的object(对象),但却需要给其一个必须的Context的话,别忘了Application Object。获取Application Context的方法很简单:执行Context.getApplicationContext()或Activity.getApplication()。

        总之,为了避免Activity Context相关的内存泄露,记住下面几条:

1)、不要长时间引用一个Activity Context(引用周期应与Acitivity的生命周期一样长)

2)、尝试使用Application Context代替Acitivity Context

3)、在Activity中,避免使用你无法控制其生命周期的非静态的内部类,使用静态的内部类,并对Activity内部进行弱引用。就是在静态的内部类中对外部类进行弱引用,就如在ViewRoot及其W内部类中的做法那样

4)、垃圾回收(GC)无法保证内存泄露

0
0

猜你在找
深度学习基础与TensorFlow实践
【在线峰会】前端开发重点难点技术剖析与创新实践
【在线峰会】一天掌握物联网全栈开发之道
【在线峰会】如何高质高效的进行Android技术开发
机器学习40天精英计划
Python数据挖掘与分析速成班
微信小程序开发实战
JFinal极速开发企业实战
备战2017软考 系统集成项目管理工程师 学习套餐
Python大型网络爬虫项目开发实战(全套)
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:723118次
    • 积分:9009
    • 等级:
    • 排名:第1931名
    • 原创:111篇
    • 转载:376篇
    • 译文:0篇
    • 评论:111条
    文章分类
    最新评论