1.memory leak

当按照下面形式使用handler时,Android studio的lint会给提示

This Handler class should be static or leaks might occur (anonymous android.os.Handler) more… (Ctrl+F1)

private static Handler handler = new Handler(){
	@Override
	public void handleMessage(Message msg) {
		//do something
	}
};

如何避免呢?参考下面写法(main thread中创建Handler的情况)

private Handler handler;

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(xxx);
	handler = new MyHandler(this);
}

static class MyHandler extends Handler {
	private final WeakReference<Activity> mActivity;

	MyHandler(Activity activity) {
		super();
		mActivity = new WeakReference<>(activity);
	}

	@Override
	public void handleMessage(Message msg) {
		Activity activity = mActivity.get();
		if (activity != null) {
			activity.handleMessage(msg);
		}
	}
}

2.Handler Looper Queue

2.1 非main thread创建Handler

handler在初始化时,通过Looper.myLooper()从ThreadLocal上去取Looper

public class Handler{
	public Handler(Callback callback, boolean async) {
		...
		mLooper = Looper.myLooper();
		if (mLooper == null) {
			throw new RuntimeException(
				"Can't create handler inside thread " + Thread.currentThread()
						+ " that has not called Looper.prepare()");
		}
		mQueue = mLooper.mQueue;
		mCallback = callback;
		mAsynchronous = async;
	}	
}

public class Looper{
	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
	public static @Nullable Looper myLooper() {
		return sThreadLocal.get();
	}
}

Handler构造方法里的异常比较常见,其中Main Thread已经帮我们创建了Looper,所以可以直接new Handler而不报错

那么,在非main thread中创建handler时,就需要像下面这样

Looper.prepare();
handler = new MyHandler(this);
Looper.loop();

2.2 Looper.prepare()

作用:创建Looper并通过ThreadLocal来实现线程绑定

private static void prepare(boolean quitAllowed) {
	if (sThreadLocal.get() != null) {
		throw new RuntimeException("Only one Looper may be created per thread");
	}
	sThreadLocal.set(new Looper(quitAllowed));
}

2.3 Looper.loop()

public class Looper{
	public static void loop() {
		final Looper me = myLooper();
		if (me == null) {
			throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
		}
		final MessageQueue queue = me.mQueue;

		// Make sure the identity of this thread is that of the local process,
		// and keep track of what that identity token actually is.
		Binder.clearCallingIdentity();
		final long ident = Binder.clearCallingIdentity();

		// Allow overriding a threshold with a system prop. e.g.
		// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
		final int thresholdOverride =
				SystemProperties.getInt("log.looper."
						+ Process.myUid() + "."
						+ Thread.currentThread().getName()
						+ ".slow", 0);

		boolean slowDeliveryDetected = false;

		for (;;) {
			Message msg = queue.next(); // might block
			if (msg == null) {
				// No message indicates that the message queue is quitting.
				return;
			}
			
			...
			
			try {
				//target is a Handler
				msg.target.dispatchMessage(msg);
				dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
			} finally {
				if (traceTag != 0) {
					Trace.traceEnd(traceTag);
				}
			}
			
			...

			msg.recycleUnchecked();
		}
	}
}

public final class Message implements Parcelable {
	    /*package*/ Handler target;
}


public class Handler {
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
}

2.4 enqueueMessage

Handler的post()和send()方法最后都会实际调用到enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

public class Handler{
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

public final class MessageQueue{
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
}