您的位置:首页 > 移动开发 > Android开发

Android Handler造成内存泄露的分析和解决

2016-04-19 23:27 453 查看
首先回顾下GC回收机制 :
Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。

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();
}

}

<span style="font-size:18px;">}</span>
这样我们就解决了Handler内存泄漏的问题。欢迎讨论...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内存泄露 Handler