几个概念
Java垃圾回收机制(GC)
Java使用自动垃圾回收机制,回收的条件就是对象是否被引用。也就是说如果对象处于不可到达状态就会被回收掉。
Java四种引用
Activity回收
Activity被销毁的时候在onDestory()方法中,系统销毁了这个Activity的实例在内存中占据的空间。在Activity的生命周期中,onDestory()方法是他生命的最后一步,资源空间等就被回收了。
Handler内存泄漏的原因?
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//修改Activity的UI
mImageView.setImageBitmap(mBitmap);
}
};
- 当使用内部类(包括匿名类)来创建Handler的时候,因为通过Handler来操作Activity中的View,所以Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用。
- Handler通常会伴随着一个耗时的后台线程(从网络下载图片),这个后台线程在任务执行完毕之后(图片下载完毕),通过消息机制通知Handler,然后Handler把图片更新到界面。
- 如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,在onDestory()方法中执行GC检查时就应该被回收掉。但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收,造成内存泄漏,直到网络请求结束(图片下载完毕)。
- 如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
- 同一个Acticity反复进行几次便会程序占用内存超过系统限制,FC。
解决方案
逻辑控制
在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
Handler声明为静态类
静态类不持有外部类的对象,所以你的Activity可以随意被回收,所以不会导致外部类实例出现内存泄露。
static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
//修改Activity的UI
mImageView.setImageBitmap(mBitmap);
}
}
但是,Handler一旦无法持有外部类的对象,就无法修改Activity的View,那么使用Handler就没有意义了。
为了解决这个问题,我们可以在Handler中增加一个对Activity的弱引用(WeakReference):
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
当Activity finish后 handler对象还是在Message中排队。 还是会处理消息,这些处理有必要?
正常Activitiy finish后,已经没有必要对消息处理,那需要怎么做呢?
解决方案也很简单,在onDestroy的时候,取消掉该Handler对象的Message和Runnable。
removeCallbacks(Runnable r)和removeMessages(int what)等。
@Override
public void onDestroy() {
//If null, all callbacks and messages will be removed.
mHandler.removeCallbacksAndMessages(null);
}