handler比较容易造成内存泄漏,所以进行改进
@SuppressWarnings("unused") public class WeakHandler { private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory private final ExecHandler mExec; private Lock mLock = new ReentrantLock(); @SuppressWarnings("ConstantConditions") final ChainedRef mRunnables = new ChainedRef(mLock, null); /** * Default constructor associates this handler with the {@link Looper} for the * current thread. * <p> * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. */ public WeakHandler() { mCallback = null; mExec = new ExecHandler(this); } /** * Constructor associates this handler with the {@link Looper} for the * current thread and takes a callback interface in which you can handle * messages. * <p> * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. * * @param callback The callback interface in which to handle messages, or null. */ public WeakHandler(Handler.Callback callback) { mCallback = callback; // Hard referencing body mExec = new ExecHandler(this); // Weak referencing inside ExecHandler } /** * Use the provided {@link Looper} instead of the default one. * * @param looper The looper, must not be null. */ public WeakHandler(Looper looper) { mCallback = null; mExec = new ExecHandler(this, looper); } /** * Use the provided {@link Looper} instead of the default one and take a callback * interface in which to handle messages. * * @param looper The looper, must not be null. * @param callback The callback interface in which to handle messages, or null. */ public WeakHandler(Looper looper, Handler.Callback callback) { mCallback = callback; mExec = new ExecHandler(this, looper); } /** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(Runnable r) { return mExec.post(wrapRunnable(r)); } /** * Causes the Runnable r to be added to the message queue, to be run * at a specific time given by <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * The runnable will be run on the thread to which this handler is attached. * * @param r The Runnable that will be executed. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean postAtTime(Runnable r, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), uptimeMillis); } /** * Causes the Runnable r to be added to the message queue, to be run * at a specific time given by <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * The runnable will be run on the thread to which this handler is attached. * * @param r The Runnable that will be executed. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. * @see android.os.SystemClock#uptimeMillis */ public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); } /** * Causes the Runnable r to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the thread to which this handler * is attached. * * @param r The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean postDelayed(Runnable r, long delayMillis) { return mExec.postDelayed(wrapRunnable(r), delayMillis); } /** * Posts a message to an object that implements Runnable. * Causes the Runnable r to executed on the next iteration through the * message queue. The runnable will be run on the thread to which this * handler is attached. * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> * * @param r The Runnable that will be executed. * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean postAtFrontOfQueue(Runnable r) { return mExec.postAtFrontOfQueue(wrapRunnable(r)); } /** * Remove any pending posts of Runnable r that are in the message queue. */ public final void removeCallbacks(Runnable r) { final WeakRunnable runnable = mRunnables.remove(r); if (runnable != null) { mExec.removeCallbacks(runnable); } } /** * Remove any pending posts of Runnable <var>r</var> with Object * <var>token</var> that are in the message queue. If <var>token</var> is null, * all callbacks will be removed. */ public final void removeCallbacks(Runnable r, Object token) { final WeakRunnable runnable = mRunnables.remove(r); if (runnable != null) { mExec.removeCallbacks(runnable, token); } } /** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in callback, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessage(Message msg) { return mExec.sendMessage(msg); } /** * Sends a Message containing only the what value. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessage(int what) { return mExec.sendEmptyMessage(what); } /** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * @see #sendMessageDelayed(Message, long) */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { return mExec.sendEmptyMessageDelayed(what, delayMillis); } /** * Sends a Message containing only the what value, to be delivered * at a specific time. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * @see #sendMessageAtTime(Message, long) */ public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { return mExec.sendEmptyMessageAtTime(what, uptimeMillis); } /** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * callback, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { return mExec.sendMessageDelayed(msg, delayMillis); } /** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * You will receive it in callback, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { return mExec.sendMessageAtTime(msg, uptimeMillis); } /** * Enqueue a message at the front of the message queue, to be processed on * the next iteration of the message loop. You will receive it in * callback, in the thread attached to this handler. * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessageAtFrontOfQueue(Message msg) { return mExec.sendMessageAtFrontOfQueue(msg); } /** * Remove any pending posts of messages with code 'what' that are in the * message queue. */ public final void removeMessages(int what) { mExec.removeMessages(what); } /** * Remove any pending posts of messages with code 'what' and whose obj is * 'object' that are in the message queue. If <var>object</var> is null, * all messages will be removed. */ public final void removeMessages(int what, Object object) { mExec.removeMessages(what, object); } /** * Remove any pending posts of callbacks and sent messages whose * <var>obj</var> is <var>token</var>. If <var>token</var> is null, * all callbacks and messages will be removed. */ public final void removeCallbacksAndMessages(Object token) { mExec.removeCallbacksAndMessages(token); } /** * Check if there are any pending posts of messages with code 'what' in * the message queue. */ public final boolean hasMessages(int what) { return mExec.hasMessages(what); } /** * Check if there are any pending posts of messages with code 'what' and * whose obj is 'object' in the message queue. */ public final boolean hasMessages(int what, Object object) { return mExec.hasMessages(what, object); } /** * Returns a new {@link Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). * If you don't want that facility, just call Message.obtain() instead. */ public final Message obtainMessage() { return mExec.obtainMessage(); } /** * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message. * * @param what Value to assign to the returned Message.what field. * @return A Message from the global message pool. */ public final Message obtainMessage(int what) { return mExec.obtainMessage(what); } /** * Same as {@link #obtainMessage()}, except that it also sets the what and obj members * of the returned Message. * * @param what Value to assign to the returned Message.what field. * @param obj Value to assign to the returned Message.obj field. * @return A Message from the global message pool. */ public final Message obtainMessage(int what, Object obj) { return mExec.obtainMessage(what, obj); } /** * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned * Message. * * @param what Value to assign to the returned Message.what field. * @param arg1 Value to assign to the returned Message.arg1 field. * @param arg2 Value to assign to the returned Message.arg2 field. * @return A Message from the global message pool. */ public final Message obtainMessage(int what, int arg1, int arg2) { return mExec.obtainMessage(what, arg1, arg2); } /** * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the * returned Message. * * @param what Value to assign to the returned Message.what field. * @param arg1 Value to assign to the returned Message.arg1 field. * @param arg2 Value to assign to the returned Message.arg2 field. * @param obj Value to assign to the returned Message.obj field. * @return A Message from the global message pool. */ public final Message obtainMessage(int what, int arg1, int arg2, Object obj) { return mExec.obtainMessage(what, arg1, arg2, obj); } public final Looper getLooper() { return mExec.getLooper(); } private WeakRunnable wrapRunnable(Runnable r) { //noinspection ConstantConditions if (r == null) { throw new NullPointerException("Runnable can't be null"); } final ChainedRef hardRef = new ChainedRef(mLock, r); mRunnables.insertAfter(hardRef); return hardRef.wrapper; } private static class ExecHandler extends Handler { private final WeakReference<WeakHandler> mBase; ExecHandler(WeakHandler base) { super(); mBase = new WeakReference<WeakHandler>(base); } ExecHandler(WeakHandler base, Looper looper) { super(looper); mBase = new WeakReference<WeakHandler>(base); } @Override public void handleMessage(Message msg) { WeakHandler base = mBase.get(); if (base != null) { if (base.mCallback != null) { base.mCallback.handleMessage(msg); } else { base.handleMessage(msg); } } } } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } private static final class WeakRunnable implements Runnable { private final WeakReference<Runnable> mDelegate; private final WeakReference<ChainedRef> mReference; WeakRunnable(WeakReference<Runnable> delegate, WeakReference<ChainedRef> reference) { mDelegate = delegate; mReference = reference; } @Override public void run() { final Runnable delegate = mDelegate.get(); final ChainedRef reference = mReference.get(); if (reference != null) { reference.remove(); } if (delegate != null) { delegate.run(); } } } private static class ChainedRef { ChainedRef next; ChainedRef prev; final Runnable runnable; final WeakRunnable wrapper; Lock lock; public ChainedRef(Lock lock, Runnable r) { this.runnable = r; this.lock = lock; this.wrapper = new WeakRunnable(new WeakReference<Runnable>(r), new WeakReference<ChainedRef>(this)); } public WeakRunnable remove() { lock.lock(); try { if (prev != null) { prev.next = next; } if (next != null) { next.prev = prev; } prev = null; next = null; } finally { lock.unlock(); } return wrapper; } public void insertAfter(ChainedRef candidate) { lock.lock(); try { if (this.next != null) { this.next.prev = candidate; } candidate.next = this.next; this.next = candidate; candidate.prev = this; } finally { lock.unlock(); } } public WeakRunnable remove(Runnable obj) { lock.lock(); try { ChainedRef curr = this.next; // Skipping head while (curr != null) { if (curr.runnable == obj) { // We do comparison exactly how Handler does inside return curr.remove(); } curr = curr.next; } } finally { lock.unlock(); } return null; } } }使用方法
private WeakHandler mHandler;
mHandler = new WeakHandler(Looper.getMainLooper(), this.mHandlerCallback);
private Handler.Callback mHandlerCallback = new Handler.Callback() { public boolean handleMessage(Message msg) { return true; } };mHandler.postDelayed(runnableRouteOverviewTimeOut, 8000);结束界面时候mUIHandler.removeCallbacksAndMessages(null);