1. 程式人生 > >去除煩人的This Handler class should be static or leaks might occur

去除煩人的This Handler class should be static or leaks might occur

現象

這個Wraning已經出現很久了,一直都沒怎麼關心,今天為了治療我的強迫症,決心要消滅專案中所有的Warning,這個Warning是我覺得很雞肋的一個,特撰此文記錄。
如果直接寫一個Handler的匿名類或者子類,就會出現”This Handler class should be static or leaks might occur”的Warning,這是為什麼呢?如果不寫成靜態的Handler就會記憶體洩露,蒙誰呢。

解決

這個Warning的詳細描述是這樣的:

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

說把Handler宣告成靜態的,在外部類中用弱引用包裝外部類的例項,在Handler初始化的時候傳進來,這樣我們就可以在這個Handler中使用外部類的成員變數啦,巴拉巴拉…一大堆,照做就是了,大概是這個樣子:

private static final class PagerHandler extends Handler {
    WeakReference<MainActivity> mMainActivityWeakReference;

    public PagerHandler(MainActivity mainActivity) {
        mMainActivityWeakReference = new
WeakReference<>(mainActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity mainActivity = mMainActivityWeakReference.get(); int childCount = mainActivity.mPagerAdapter.getCount(); int nextItem = mainActivity.mViewPager.getCurrentItem() + 1
; if (nextItem == childCount) { nextItem = 0; } mainActivity.mViewPager.setCurrentItem(nextItem,true); mainActivity.mPagerHandler.sendEmptyMessageDelayed(PAGER_TICK,3000); } }

我比較偷懶,在Handler內部包裝的弱引用,人家上邊要求的是在外部類中包裝自身的弱引用直接傳入到Handler的構造,其實都可以達到一樣效果的。這個無所謂~~~

原因

原因是在ADT20中添加了一個Lint檢查:
Look for handler leaks: This check makes sure that a handler inner class does not hold an implicit reference to its outer class.
這個Lint檢查的是確保內部類Handler不會持有外部類的隱式物件,我們一般的Handler確實就是這樣用的,所以正好落入套中。
當然我們可以關閉這個Lint檢查,位於Settings->Editor->Inspections->Android->Lint->Performance->Handler reference leaks,去掉後面的勾即可,但是Lint畢竟是為我們好,再說了萬一真的會有記憶體洩露發生呢?
那到底有沒有記憶體洩露發生呢?有可能。
因為如果Handler位於主執行緒,則使用的是主執行緒的Looper和MessageQueue,那麼每一個Message的target就是這個Handler,持有這個Handler的引用,假設這個時候GC要回收當前的Activity或者Service,但是由於Message太多處理不過來,或者Message延遲過大導致等待,那麼這個Handler則不會被回收,因為它被Message引用,而同時我們的Handler又是個內部類,持有外部Activity或者Service的引用,而且又不是static的,那麼外部Activity或者Service也不會被回收,所以就釋放不了啦,洩露啦。
使用弱引用之後,Handler和外部的Activity或者Service的引用性變低,就好像以前用鋼絲繩困在一起的,現在換棉線了,GC的時候就一定會回收掉弱引用包裝的物件,這個是弱引用的特點,不太懂的話就去看看Java弱引用的相關資料。
That’s All!