以前在网上也看过类似的译文,但也忘得差不多了. 直至最近在官网再次看到原文, 虽是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);
}
这是其中一个最简单的泄露上下文(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 )
- 系统的垃圾回收机制并不能保证内存不泄露