美文网首页
Handler究竟是如何泄漏内存的

Handler究竟是如何泄漏内存的

作者: 画十 | 来源:发表于2017-11-14 16:05 被阅读29次

    常见有内存泄漏的写法

    Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                    super.handleMessage(msg);
            }
    };
    

    解决方案

    private final MyHandler handler = new MyHandler(this);
    private static class MyHandler extends Handler {
            private final WeakReference<Activity> weakAcitivity;
            public MyHandler(Activity activity) {
                    weakAcitivity = new WeakReference<Activity>(activity);
            }
            @Override
            public void handleMessage(Message msg) {
                    final Activity activity = weakAcitivity.get();
                    if (null != activity) {
                            // coding
                    }
            }
      }
    

    为什么会出现内存泄漏

    注:以下均以在主线程new Handler()举例,在其他线程初始化Handler情况类似

    跨进程原理概述

    Handler跨线程原理是主线程中持有一个MessageQueue,在新线程中对这个队列进行插入(sendMessage()),然后在主线程循环读取然后通过调用callback,再调用handleMessage()实现跨线程的。

    内存泄漏原理概述

    因为很有可能会有好几个Handler同时向主线程MessageQueue 插入数据,而callback的时候需要回调到各自的Handler中去,所以插入MessageQueue中的实例Message会持有当前handler实例。而MessageQueue的生命周期是和当前主线程的生命周期一致的,如果Acitivity持有handler的话,那就必然会造成内存泄漏了。

    源码分析
    1. 所有的sendMessage()最终都会调用下面代码

      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);
      }
      
    2. 而在enqueueMessage()中,它会把this,也就是当前handler保存在msg中,然后插入到当前主线程的MessageQueue中。到此为止,真相大白。

      private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
              msg.target = this;
              if (mAsynchronous) {
                      msg.setAsynchronous(true);
              }
              return queue.enqueueMessage(msg, uptimeMillis);
      }
      
    3. 回调callback,在Looper.java中不断循环MessageQueue从中取出Message(只显示了部分关键代码)

      public static void loop() {
              for (;;) {
                      try {
                              msg.target.dispatchMessage(msg);
                              end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                      } finally {
                              if (traceTag != 0) {
                                      Trace.traceEnd(traceTag);
                              }
                      }
              }
      }
      
    4. 回到Handler中,看看被Looper调用的dispatchMessage()方法:

      public void dispatchMessage(Message msg) {
               if (msg.callback != null) {
                       handleCallback(msg);
               } else {
                       if (mCallback != null) {
                               if (mCallback.handleMessage(msg)) {
                                       return;
                               }
                       }
                       handleMessage(msg);
               }
      }
      
    5. 至此,真相得到了验证。

    相关文章

      网友评论

          本文标题:Handler究竟是如何泄漏内存的

          本文链接:https://www.haomeiwen.com/subject/aahfvxtx.html