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

Android内存优化

2016-06-27 08:34 736 查看
觉得有用,就转一下了...

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

[java] view
plain copy

private Handler handler;  

handler = new Handler(){  

    @Override  

    public void handleMessage(Message msg) {  

        // TODO Auto-generated method stub  

        super.handleMessage(msg);  

    }  

};  

Message message = new Message();  

message.what = 1;  

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

   public static void prepareMainLooper() {  

    //调用prepare创建Looper  

       prepare(false);  

       synchronized (Looper.class) {  

           if (sMainLooper != null) {  

               throw new IllegalStateException("The main Looper has already been prepared.");  

           }  

           sMainLooper = myLooper();  

       }  

   }  

  

   private static void prepare(boolean quitAllowed) {  

       if (sThreadLocal.get() != null) {  

           throw new RuntimeException("Only one Looper may be created per thread");  

       }  

    //这里new了一个Looper对象  

       sThreadLocal.set(new Looper(quitAllowed));  

   }  

//sThreadLocal是个静态变量  

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  

Looper中有一个MessageQueue对象,保存着消息队列

[java] view
plain copy

final MessageQueue 
10792
mQueue;  

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

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

[java] view
plain copy

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  

    MessageQueue queue = mQueue;  

    if (queue == null) {  

        RuntimeException e = new RuntimeException(  

                this + " sendMessageAtTime() called with no mQueue");  

        Log.w("Looper", e.getMessage(), e);  

        return false;  

    }  

    return enqueueMessage(queue, msg, uptimeMillis);  

}  

  

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  

//这里就是,消息持有了handler对象,handler持有Activity  

    msg.target = this;  

    if (mAsynchronous) {  

        msg.setAsynchronous(true);  

    }  

    return queue.enqueueMessage(msg, uptimeMillis);  

}  

当Handler创建时,会关联到上面创建的Looper对象,这样消息机制就可以运转起来了。

[java] view
plain copy

  public Handler(Callback callback, boolean async) {  

      if (FIND_POTENTIAL_LEAKS) {  

          final Class<? extends Handler> klass = getClass();  

    //看看这里,如果是匿名类或者成员类或者局部类,并且不是静态对象,系统就会提示警告,有内存泄露的危险  

          if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  

                  (klass.getModifiers() & Modifier.STATIC) == 0) {  

              Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  

                  klass.getCanonicalName());  

          }  

      }  

//取出Looper  

      mLooper = Looper.myLooper();  

      if (mLooper == null) {  

          throw new RuntimeException(  

              "Can't create handler inside thread that has not called Looper.prepare()");  

      }  

      mQueue = mLooper.mQueue;  

      mCallback = callback;  

      mAsynchronous = async;  

  }  

知道了上面的原因,那么为了避免内存泄露,我们可以采用以下方法,具体实现网上有很多文章,这里不再罗列。

1、用static声明handler,静态类不会引用外部类

2、如果Handler中必须用到Activity,那就用WeakReference去引用

3、在Activity结束或暂停的事件中,removeMessages或者removeCallbacksAndMessages将消息队列中的消息移除(避免满足上面两条后,当Activity关闭了,但是Handler还未处理到,造成内存泄露)

源地址:http://blog.csdn.net/bdmh/article/details/49251849
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: