首先回顾下GC回收机制 :
Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。
Handler造成内存泄漏的原因(如果对Handle机制不太了解的,建议先查看上篇写的Handler机制):
当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。
解决方案:
我们可以通过将Handler设置为static,静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)。
具体代码如下:
package com.handler;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.widget.ImageView;
/**
* 使用Handler造成内存泄露的分析和解决
*
* @author zhongyao
*
*/
public class MainActivity extends ActionBarActivity {
private ImageView iv;
private final static int SUCCESS = 1;
private String imgUrl = "http://m3.biz.itc.cn/pic/new/n/54/56/Img5435654_n.jpg";
static class MyHandler extends Handler {
private final WeakReference<Activity> mActivityReference;
MyHandler(Activity activity) {
mActivityReference = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
final MainActivity activity = (MainActivity) mActivityReference.get();
if (activity != null) {
switch (msg.what) {
case SUCCESS:
Bitmap bitmap = (Bitmap) msg.obj;
activity.iv.setImageBitmap(bitmap);
break;
default:
break;
}
}
}
}
private final MyHandler handler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
new Thread(new Runnable() {
@Override
public void run() {
doTask();
}
}).start();
}
protected void doTask() {
try {
HttpGet get = new HttpGet(imgUrl);
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
InputStream inputStream = response.getEntity().getContent();
if (inputStream != null) {
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream);
Message msg = new Message();
msg.obj = bitmap;
msg.what = SUCCESS;
handler.sendMessage(msg);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
这样我们就解决了Handler内存泄漏的问题。欢迎讨论...
补充另一种较好的方法:
private Handler mHandle = new Handler();
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
finish();
}
}
};
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (mHandle != null && mRunnable != null) {
mHandle.postDelayed(mRunnable, 500);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHandle != null && mRunnable != null) {
mHandle.removeCallbacks(mRunnable);
//或者
//mHandle.removeCallbacksAndMessages(null);
}
ShareLogger.logD("WeiboShareCallbackActivity-->onDestroy");
}