Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler

/ / start from here handler. SendMessageDelayed (message, 1 * 60 * 1000); Public final Boolean sendMessageDelayed(@nonnull Message MSG, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public Boolean sendMessageAtTime(@nonnull 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(@nonnull MessageQueue queue, @nonNULL Message MSG, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }Copy the code

The core problem is enqueueMessage, where msg.target = this points to the current Handler, which can never be collected by gc (because of references). If an inner class Handler cannot be recycled, then an outer class Activity cannot be recycled either.

Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler Handler

public static void loop() { // ... Filter other code for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } / /... Filter the other code MSG. Target. DispatchMessage (MSG); / /... Filter other code msg.recycleunchecked (); }}Copy the code

In the loop () by MSG. Target. DispatchMessage handleMessage (MSG) trigger Handler, and then through the MSG. RecycleUnchecked on current MSG () to remove references

void recycleUnchecked() { flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = UID_NONE; workSourceUid = UID_NONE; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; }}}Copy the code

In recycleUnchecked(), reset MSG targets to NULL with target = null, thus removing the Handler reference, and the GC will recycle the Handler normally

Now that the source code has been analyzed, we can know the cause of the leak in our daily use: The handler sends a delayed message via sendMessageDelayed. The delay is so long that the current Activity may be shut down, so that the GC cannot reclaim the handler before the message is consumed, and because the Handler, as a non-static inner class, holds references to the outer class, Activities are also not collected by the GC.

There are three solutions:

  • 1: Use staticHandlerAnd then throughweakreferenceThe incomingActivitySo that we can guaranteeActivityIt gets recycled, butHandlerStill not recyclable
  • 2: Use as an external classHandlerAnd then throughweakreferenceThe incomingActivity. This is exactly the same thing
  • 3:ActivitytheonDestroy()Method.handler.removeCallbacksAndMessages(null)Target (MSG); target (MSG)handlerIt will be collected directly by gc and thenActivityIt gets recycled

Let’s look at handler. RemoveCallbacksAndMessages (null) what did:

/** * remove all MSG from obj token if token is null Remove all MSG * / public final void removeCallbacksAndMessages (@ Nullable Object token) { mQueue.removeCallbacksAndMessages(this, token); } void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p ! = null && p.target == h && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; // Empty the list of references p.ricycleunchecked (); p = n; } // Remove all messages after front. while (p ! = null) { Message n = p.next; if (n ! = null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; // Empty the references to the MSG n.recycleunchecked (); p.next = nn; continue; } } p = n; }}}Copy the code

As you can see, the msg.recycleunchecked () call cleans up all references to the current MSG, thus solving the memory leak