PS:本文摘抄自《Android高級進階》,僅供學習使用
Android代碼中涉及線程間通信的地方經常會使用Handler,典型的代碼結構如下。
public class HandlerActivity extends Activity{ //可能引入內存泄漏的用法 private final Handler mLeakyHandler = new Handler(){ @Orrvide public void handleMessage(Mesage msg){ //... } }; }
使用Android Lint分析這段代碼,會違反檢測項AndroidLintHandlerLeak,得到如下提示。
This Handler class should be static or leaks might occur.
那么產生內存泄漏的原因可能是什么呢?我們知道,Handler是和Looper以及MessageQueue一起工作的,在Android中,一個應用啟動后,系統默認會創建一個為主線程服務的Looper對象,該Looper對象用於處理主線程的所有Message對象,它的生命周期貫穿於整個應用的生命周期。在主線程中使用的Handler都會默認綁定到這個Looper對象。在主線程中創建Handler對象,它會立即關聯到主線程Looper對象的MessageQueue,這時發送到MessageQueue中的Message對象都會只有這個Handler對象的引用,這樣在Looper處理消息時常能回調到Handler的handlerMessage方法。因此,如果Message還沒有被處理完成,那么Handler對象也就不會被垃圾回收。
在上面的代碼中,將Handler的實例聲明為HandlerActivity類的內部類。而在Java語言中,非靜態內部匿名類會持有外部類的一個隱式的引用,這樣就可能會導致外部類無法被垃圾回收。因此,最終由於MessageQueue中Message還沒處理完成,就會持有Handler對象的引用,而非靜態的Handler對象會持有外部類HandlerActivity的引用,這個Activity無法被垃圾回收,從而導致內存泄漏。
一個明顯的會引入內存泄漏的例子如下。
public class HandlerActivity extends Activity{ //可能引入內存泄漏的用法 private final Handler mLeakyHandler = new Handler(){ @Orrivide public void handleMessage(Mesage msg){ //... } }; @Orrivide protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); //延遲5分鍾發送消息 mLeakyHandler.postDelayed(new Runnable(){ @Orrivide public void run(){/* ... */} }, 1000*60*5); } }
由於消息延長5分鍾,因此,當用戶進入這個Activity並退出后,在消息發送並處理完成之前,這個Activity是不會被系統回收的(系統內存確實不夠使用的情況例外)。
如果解決呢。有兩個方案。
- 在子線程中使用Handler,這時需要開發者自己創建一個Looper對象,這個Looper對象的生命周期同一般的Java對象,因此這種用法沒有問題。
- 將Handler生命為靜態的內部類,前面說過,靜態內部類不會持有外部類的引用,英寸,野不會引起內存泄漏,經典用法的代碼如下。
public class HandlerAcitivity extends Activity{ //聲明一個靜態的Handler內部類,並持有外部類的弱引用 private static class InnerHandler extends Handler{ private final WeakReference<HandlerAcitivity> mActivity; public InnerHandler(HandlerAcitivity activity){ mActivity = new WeakReference<HandlerAcitivity>(activity); } @Override public void handleMessage(Message msg){ HandlerAcitivity activity = mActivity.get(); if(activity!=null){ //... } } } private final InnerHandler mHandler = new InnerHandler(this); //靜態的匿名內部類不會持有外部類的引用 private static final Runnable sRunnable = new Runnable(){ @Override public void run(){ //... } }; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); //延遲5分鍾發送消息 mHandler.postDelay(sRunnable, 1000*60*5); } }