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

来源:互联网 发布:mysql主键与索引 编辑:程序博客网 时间:2024/06/05 11:32

转载自:http://blog.csdn.net/qq_23547831/article/details/46881941

(一)什么是内存泄露?

Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。但是有时候我们使用内存对象在需要销毁的时候这个对象因为被其他对象引用而无法销毁,那么这时候这个对象就属于内存泄露了。


(二)handler的基本用法与内存泄露原因

1)在主线程中定义handler并重写handlerMessage方法

[html] view plain copy
print?
  1. Handler handler = new Handler() {  
  2.   
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             dosomething();  
  6.   
  7.         }  
  8.     };  
Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            dosomething();        }    };

2)在线程中调用

[html] view plain copy
print?
  1. handler.sendEmptyMessage()  
handler.sendEmptyMessage()

这样就实现了子线程与主线程的通信;


但是这种看似正常的写法其实有一些不可预知的错误;我们定义的handler对象其实是一个内部匿名了它会隐式的持有外部类的引用,我们可以看到

[html] view plain copy
print?
  1. public boolean isGoToComplete = false;  
  2.   
  3.     Handler handler = new Handler() {  
  4.   
  5.         @Override  
  6.         public void handleMessage(Message msg) {  
  7.             if (isGoToComplete == false) {  
  8.                   
  9.             }  
  10.   
  11.         }  
  12.     };  
public boolean isGoToComplete = false;    Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (isGoToComplete == false) {            }        }    };
它默认是可以使用外部类的成员变量的,这样也佐证了我们所说的它会隐式的持有外部类的引用;
这时候如果子线程使用handler将message消息发送到messageQueue中并等待执行的过程过长(具体异步消息相关的内容可以查看:http://blog.csdn.net/qq_23547831/article/details/41697039),这时候activity已经执行finish方法;

那么我们希望的是activity被执行onDestory方法,然后activity相关的各种资源,组件都被销毁掉,但是由于handler隐式的持有activity的引用,那么activity就无法被回收,activity相关的资源与组件也无法被回收–即内存已经泄露。


(三)解决方法

1)使用静态变量定义handler

[html] view plain copy
print?
  1. static Handler handler = new Handler() {  
  2.   
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             dosomething();  
  6.   
  7.         }  
  8.     };  
static Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            dosomething();        }    };

这样的话,handler对象由于是静态类型无法持有外部activity的引用,但是这样Handler不再持有外部类的引用,导致程序不允许在Handler中操作activity中的对象了,这时候我们需要在Handler对象中增加一个队activity的弱引用;

[html] view plain copy
print?
  1. static class MyHandler extends Handler {  
  2.     WeakReference<Activity > mActivityReference;  
  3.     MyHandler(Activity activity) {  
  4.         mActivityReferencenew WeakReference<Activity>(activity);  
  5.     }  
  6.     @Override  
  7.     public void handleMessage(Message msg) {  
  8.         final Activity activity = mActivityReference.get();  
  9.         if (activity != null) {  
  10.             mImageView.setImageBitmap(mBitmap);  
  11.         }  
  12.     }  
  13. }  
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);        }    }}


弱引用相关的知识可参考:http://blog.csdn.NET/qq_23547831/article/details/46505287


2)通过代码逻辑判断

在activity执行onDestory时,判断是否有handler已经执行完成,否则做相关逻辑操作;

阅读全文
0 0
原创粉丝点击