Handler究竟是如何泄漏内存的

2017-11-14  本文已影响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. 至此,真相得到了验证。

上一篇 下一篇

猜你喜欢

热点阅读