Android性能优化 - 避免内存泄露

以前在网上也看过类似的译文,但也忘得差不多了. 直至最近在官网再次看到原文, 虽是09年的文章, 略显久远, 但再看一次还是觉得总结很好. 于是决定翻译下来, 顺便巩固自己的相关知识.


原文链接


安卓应用在大多数机型( 针对以前的比较旧的机型, 现在的手机配置越来越高, 可分配的运行内存也相应会比以前提高) 会得到16MB的应用内存. 即使你不打算真的使用这全部的内存, 你也应该尽可能的使用更少的内存, 以免因为占用内存过大而进程被系统杀死. Android手机内存中(短暂)保留的应用更多, 那么用户在这些被保留的应用间互相切换时就会更快(得到响应). 工作中, 碰到内存泄露问题是会碰到的情况, 并且这种情况大多数都是因为时间过长的( 对象,资源等 )引用存在于应用上下文(Context)中.


在Android中, 一个Context被用于很多的操作中, 但大多数是用于加载和使用资源, 这也是为什么所有组件(Widgets)在构造器中都需要接收一个Context参数. 在一般的Android应用中, 你通常会拥有2种Context, Activity和 Applicatin. 这也通常是开发人员首先要传到类和方法中的Context:

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


这意味着, 视图(Views)有一个在整个Activity中的引用, 也因此该Activity会(紧紧)保持这些Views: 通常是整个View所有层级的父View和子View,以及这些View所用到的资源. 因此, 如果你泄露(leak)了上下文(Context).(这里的"泄露" 是指你保持了一个避免被GC - 垃圾回收机制回收 的引用 ), 逐渐的会变成泄露了很多内存. 这样下去, 如果你稍不注意, 泄露一整个Activity的内存是很容易发生的事情.


当屏幕横竖屏切换, 系统会默认销毁当前Activity, 然后会根据旧Activity被销毁前被保存的状态(要开发者在销毁前手动保存状态才有效)去创建一个新的Activity. 这样, Android将会重载应用的UI布局和资源的. 现在你不想应用在每一个方向(竖屏, 横屏)都加载一个大的位图(bitmap).  那么保持这个位图, 不在每次倒置屏幕时都去重载它的最简单的方法就是将它设为静态(static):

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 会被设置一个Drawable的回调. 在上面的代码块中, 这个意味着drawable在TextView中有一个应用, 而这个TextView本身在Activity(上下文)中又有一个引用. 类似这样, Activity就保持着各种资源的引用(当然这取决于你的代码).


这是其中一个最简单的泄露上下文(Context)内存的例子, 你能在这里( 在主屏幕源码 Home screen's source code 查找 unbindDrawables()方法)看到它是怎么工作的. 当Activity被销毁时, 将(引用的)存储的drawable回调为null. 有趣的是, 在某些你创建了资源间互相引用的"链"的情况下这么做是不好的, 反而使应用更快的耗尽内存.


这里有2个简单的方法去避免相关的上下文内存泄露. 最显著的一个方法是避免资源脱离自身所在的上下文范围(而存在). 上面的例子展示了静态引用的案例, 内部类和该类包含的引用对其他类可能是同样的危险. 第二个解决方案是是适用 Application  这个上下文对象. Application 会一直在应用运行时存在, 并且不会像Activity受其生命周期影响. 如果你打算保持一个长期使用的对象, 就需要一个这样的上下文(Context). 你可以轻易的通过调用 Context.getApplicationContext() 和 Activity.getApplication() 方法来获得它.


总结, 为了避免相关上下文泄露内存, 请记得以下的几点:

  • 在Activity中不要保持长时间的引用(引用应该跟随同样的Activity生命周期一样, Activity销毁, 引用也置空)
  • (对于某些需要长时间保持的引用)尝试使用Application代替Activity
  • 如果你不能控制类的生命周期, 则避免在Activity中使用非静态的内部类. 最好在Activity中使用静态内部类和弱引用. 这些问题的解决方案是一个被外部类被使用的内部类应当使用 WeakReference 弱引用(相关示例可看 ViewRoot )
  • 系统的垃圾回收机制并不能保证内存不泄露



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值