1. 程式人生 > >Weak Handler 與 記憶體洩露

Weak Handler 與 記憶體洩露

一個通用的場景是 使用 匿名內部類 例項 作為某個 行為/動作的 回撥,如果該行為/動作 是非同步的,則其返回時間往往無法確定,有造成記憶體洩露風險.

使用靜態內部類,或者妥善處理生命週期,都不會造成記憶體洩露,反過來,當沒有記憶體洩露風險時,一般直接匿名內部類即可.

這其實是一個特別矛盾的說法.

因為這要求程式設計師能 能瞭解到 回撥行為是在何時發生的.

而相反,我們設計介面回撥的時候總是盡力遮蔽內部實現細節.

而面對不確定的行為,當然也可以使用靜態內部類,或者removeCallback去取消回撥(特指Handler),然而這樣程式碼變得繁瑣,或者新增不必要的程式碼.

對於Handler,一個簡單的第三方解決方案是使用 android-weak-handler ,該庫嘗試使用WeakReference解決這個問題,然而卻引入新的問題.

核心程式碼如下:

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")
    @VisibleForTesting
    final
ChainedRef mRunnables = new ChainedRef(mLock, null); public WeakHandler() { mCallback = null; mExec = new ExecHandler(); } public WeakHandler(@Nullable Handler.Callback callback) { mCallback = callback; // Hard referencing body mExec = new ExecHandler(new
WeakReference<>(callback)); // Weak referencing inside ExecHandler } public WeakHandler(@NonNull Looper looper) { mCallback = null; mExec = new ExecHandler(looper); } public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { mCallback = callback; mExec = new ExecHandler(looper, new WeakReference<>(callback)); } public final boolean post(@NonNull Runnable r) { return mExec.post(wrapRunnable(r)); } public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), uptimeMillis); } public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); } public final boolean postDelayed(Runnable r, long delayMillis) { return mExec.postDelayed(wrapRunnable(r), delayMillis); } public final boolean postAtFrontOfQueue(Runnable r) { return mExec.postAtFrontOfQueue(wrapRunnable(r)); } public final void removeCallbacks(Runnable r) { final WeakRunnable runnable = mRunnables.remove(r); if (runnable != null) { mExec.removeCallbacks(runnable); } } public final void removeCallbacks(Runnable r, Object token) { final WeakRunnable runnable = mRunnables.remove(r); if (runnable != null) { mExec.removeCallbacks(runnable, token); } } public final boolean sendMessage(Message msg) { return mExec.sendMessage(msg); } public final boolean sendEmptyMessage(int what) { return mExec.sendEmptyMessage(what); } public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { return mExec.sendEmptyMessageDelayed(what, delayMillis); } public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { return mExec.sendEmptyMessageAtTime(what, uptimeMillis); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { return mExec.sendMessageDelayed(msg, delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { return mExec.sendMessageAtTime(msg, uptimeMillis); } public final boolean sendMessageAtFrontOfQueue(Message msg) { return mExec.sendMessageAtFrontOfQueue(msg); } public final void removeMessages(int what) { mExec.removeMessages(what); } public final void removeMessages(int what, Object object) { mExec.removeMessages(what, object); } public final void removeCallbacksAndMessages(Object token) { mExec.removeCallbacksAndMessages(token); } public final boolean hasMessages(int what) { return mExec.hasMessages(what); } public final boolean hasMessages(int what, Object object) { return mExec.hasMessages(what, object); } public final Looper getLooper() { return mExec.getLooper(); } private WeakRunnable wrapRunnable(@NonNull 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<Handler.Callback> mCallback; ExecHandler() { mCallback = null; } ExecHandler(WeakReference<Handler.Callback> callback) { mCallback = callback; } ExecHandler(Looper looper) { super(looper); mCallback = null; } ExecHandler(Looper looper, WeakReference<Handler.Callback> callback) { super(looper); mCallback = callback; } @Override public void handleMessage(@NonNull Message msg) { if (mCallback == null) { return; } final Handler.Callback callback = mCallback.get(); if (callback == null) { // Already disposed return; } callback.handleMessage(msg); } } static 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(); } } } static class ChainedRef { @Nullable ChainedRef next; @Nullable ChainedRef prev; @NonNull final Runnable runnable; @NonNull final WeakRunnable wrapper; @NonNull Lock lock; public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { this.runnable = r; this.lock = lock; this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(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(@NonNull 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(); } } @Nullable 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; } } }

這個類的實現有個問題,為了避免持