Android内存优化

来源:互联网 发布:pk10软件平刷王 编辑:程序博客网 时间:2024/06/07 06:02

觉得有用,就转一下了...

在避免内存泄露的文章中,Handler经常被提起,原因就是对象的强引用,比如一个Activity内部有一个Handler对象在运行

[java] view plain copy
  1. private Handler handler;  
  2. handler = new Handler(){  
  3.     @Override  
  4.     public void handleMessage(Message msg) {  
  5.         // TODO Auto-generated method stub  
  6.         super.handleMessage(msg);  
  7.     }  
  8. };  
  9. Message message = new Message();  
  10. message.what = 1;  
  11. handler.sendMessage(message);  
当我们使用上面代码创建一个Handler时,IDE就会给我提示

This Handler class should be static or leaks might occur (com.example.androidtest.MainActivity.2)
告诉我们,会产生内存泄露。

当Activity关闭时,Handler不一定处理完毕,但是Handler对Activity有引用关系,导致GC无法回收Activity对象,造成内存泄露。那么Handler为什么会这样呢,那就看看它的实现吧。

在Handler对象中会隐式的引用到Activity,这就形成了强引用,也是造成内存泄露的原因。
Handler是用来处理消息的,那么就要有一个对象进行消息的分发,这就是Looper
当进程启动时,ActivityThread会创建一个Looper对象,Looper对象的研究,参考老罗的文章 http://blog.csdn.net/luoshengyang/article/details/6817933
[java] view plain copy
  1.    public static void prepareMainLooper() {  
  2.     //调用prepare创建Looper  
  3.        prepare(false);  
  4.        synchronized (Looper.class) {  
  5.            if (sMainLooper != null) {  
  6.                throw new IllegalStateException("The main Looper has already been prepared.");  
  7.            }  
  8.            sMainLooper = myLooper();  
  9.        }  
  10.    }  
  11.   
  12.    private static void prepare(boolean quitAllowed) {  
  13.        if (sThreadLocal.get() != null) {  
  14.            throw new RuntimeException("Only one Looper may be created per thread");  
  15.        }  
  16.     //这里new了一个Looper对象  
  17.        sThreadLocal.set(new Looper(quitAllowed));  
  18.    }  
  19. //sThreadLocal是个静态变量  
  20. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  

Looper中有一个MessageQueue对象,保存着消息队列
[java] view plain copy
  1. final MessageQueue mQueue;  

当Handler发送消息时,消息会被加入到MessageQueue中,并且Message的target对象和Handler进行了绑定,这样这个消息也就对Activity进行了引用,只要这个消息在,GC就无法回收已经关闭的Activity

handler.sendMessage发送消息最终调用下面的方法,持有了Activity

[java] view plain copy
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  2.     MessageQueue queue = mQueue;  
  3.     if (queue == null) {  
  4.         RuntimeException e = new RuntimeException(  
  5.                 this + " sendMessageAtTime() called with no mQueue");  
  6.         Log.w("Looper", e.getMessage(), e);  
  7.         return false;  
  8.     }  
  9.     return enqueueMessage(queue, msg, uptimeMillis);  
  10. }  
  11.   
  12. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  13. //这里就是,消息持有了handler对象,handler持有Activity  
  14.     msg.target = this;  
  15.     if (mAsynchronous) {  
  16.         msg.setAsynchronous(true);  
  17.     }  
  18.     return queue.enqueueMessage(msg, uptimeMillis);  
  19. }  

当Handler创建时,会关联到上面创建的Looper对象,这样消息机制就可以运转起来了。
[java] view plain copy
  1.   public Handler(Callback callback, boolean async) {  
  2.       if (FIND_POTENTIAL_LEAKS) {  
  3.           final Class<? extends Handler> klass = getClass();  
  4.     //看看这里,如果是匿名类或者成员类或者局部类,并且不是静态对象,系统就会提示警告,有内存泄露的危险  
  5.           if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  6.                   (klass.getModifiers() & Modifier.STATIC) == 0) {  
  7.               Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  8.                   klass.getCanonicalName());  
  9.           }  
  10.       }  
  11. //取出Looper  
  12.       mLooper = Looper.myLooper();  
  13.       if (mLooper == null) {  
  14.           throw new RuntimeException(  
  15.               "Can't create handler inside thread that has not called Looper.prepare()");  
  16.       }  
  17.       mQueue = mLooper.mQueue;  
  18.       mCallback = callback;  
  19.       mAsynchronous = async;  
  20.   }  

知道了上面的原因,那么为了避免内存泄露,我们可以采用以下方法,具体实现网上有很多文章,这里不再罗列。
1、用static声明handler,静态类不会引用外部类
2、如果Handler中必须用到Activity,那就用WeakReference去引用
3、在Activity结束或暂停的事件中,removeMessages或者removeCallbacksAndMessages将消息队列中的消息移除(避免满足上面两条后,当Activity关闭了,但是Handler还未处理到,造成内存泄露)


源地址:http://blog.csdn.net/bdmh/article/details/49251849


1 0
原创粉丝点击