【譯】什么導致了Context泄露:Handler內部類

思考下面代碼

復制代碼
1 public class SampleActivity extends Activity {
2 
3   private final Handler mLeakyHandler = new Handler() {
4     @Override
5     public void handleMessage(Message msg) {
6       // ... 
7     }
8   }
9 }
復制代碼

如果沒有仔細觀察,上面的代碼可能導致嚴重的內存泄露。Android Lint會給出下面的警告:

In Android, Handler classes should be static or leaks might occur.

但是到底是泄漏,如何發生的?讓我們確定問題的根源,先寫下我們所知道的
1、當一個Android應用程序第一次啟動時,Android框架為應用程序的主線程創建一個Looper對象。一個Looper實現了一個簡單的消息隊列,在一個循環中處理Message對象。所有主要的應用程序框架事件(如活動生命周期方法調用,單擊按鈕,等等)都包含在Message對象,它被添加到Looper的消息隊列然后一個個被處理。主線程的Looper在應用程序的整個生命周期中存在。
2、當一個Handle在主線程被實例化,它就被關聯到Looper的消息隊列。被發送到消息隊列的消息會持有一個Handler的引用,以便Android框架可以在Looper最終處理這個消息的時候,調用Handler#handleMessage(Message)。
3、在Java中,非靜態的內部類和匿名類會隱式地持有一個他們外部類的引用。靜態內部類則不會。

那么,到底是內存泄漏?好像很難懂,讓我們以下面的代碼作為一個例子

復制代碼
 1 public class SampleActivity extends Activity {
 2  
 3   private final Handler mLeakyHandler = new Handler() {
 4     @Override
 5     public void handleMessage(Message msg) {
 6       // ...
 7     }
 8   }
 9  
10   @Override
11   protected void onCreate(Bundle savedInstanceState) {
12     super.onCreate(savedInstanceState);
13  
14     // 延時10分鐘發送一個消息
15     mLeakyHandler.postDelayed(new Runnable() {
16       @Override
17       public void run() { }
18     }, 60 * 10 * 1000);
19  
20     // 返回前一個Activity
21     finish();
22   }
23 }
復制代碼

?

當這個Activity被finished后,延時發送的消息會繼續在主線程的消息隊列中存活10分鐘,直到他們被處理。這個消息持有這個Activity的Handler引用,這個Handler有隱式地持有他的外部類(在這個例子中是SampleActivity)。直到消息被處理前,這個引用都不會被釋放。因此Activity不會被垃圾回收機制回收,泄露他所持有的應用程序資源。注意,第15行的匿名Runnable類也一樣。匿名類的非靜態實例持有一個隱式的外部類引用,因此context將被泄露。

為了解決這個問題,Handler的子類應該定義在一個新文件中或使用靜態內部類。靜態內部類不會隱式持有外部類的引用。所以不會導致它的Activity泄露。如果你需要在Handle內部調用外部Activity的方法,那么讓Handler持有一個Activity的弱引用(WeakReference)以便你不會意外導致context泄露。為了解決我們實例化匿名Runnable類可能導致的內存泄露,我們將用一個靜態變量來引用他(因為匿名類的靜態實例不會隱式持有他們外部類的引用)。

復制代碼
 1 public class SampleActivity extends Activity {
 2     /**
 3     * 匿名類的靜態實例不會隱式持有他們外部類的引用
 4     */
 5     private static final Runnable sRunnable = new Runnable() {
 6             @Override
 7             public void run() {
 8             }
 9         };
10 
11     private final MyHandler mHandler = new MyHandler(this);
12 
13     @Override
14     protected void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16 
17         // 延時10分鐘發送一個消息.
18         mHandler.postDelayed(sRunnable, 60 * 10 * 1000);
19 
20         // 返回前一個Activity
21         finish();
22     }
23 
24     /**
25     * 靜態內部類的實例不會隱式持有他們外部類的引用。
26     */
27     private static class MyHandler extends Handler {
28         private final WeakReference<SampleActivity> mActivity;
29 
30         public MyHandler(SampleActivity activity) {
31             mActivity = new WeakReference<SampleActivity>(activity);
32         }
33 
34         @Override
35         public void handleMessage(Message msg) {
36             SampleActivity activity = mActivity.get();
37 
38             if (activity != null) {
39                 // ...
40             }
41         }
42     }
43 }
復制代碼

靜態和非靜態內部類的區別是比較難懂的,但每一個Android開發人員都應該了解。開發中不能碰的雷區是什么?不在一個Activity中使用非靜態內部類, 以防它的生命周期比Activity長。相反,盡量使用持有Activity弱引用的靜態內部類。

譯文鏈接


作者:kissazi2?
出處:http://www.cnblogs.com/kissazi2/?
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

轉載:http://www.cnblogs.com/kissazi2/p/4121852.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458470.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458470.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458470.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

js基礎 one

js忽略空格符和換行符 js嚴格區分大小寫 &#xff1b;為js的結束符 可以使用{}擴成一個語句組&#xff0c;形成一個block塊 通過 \ 實現折行操作 document.write(hello \world); 通過document.write() 向文檔書寫內容 通過xonsole.log()向控制臺寫入內容變量 js變量重名會產…

關于.Net中Process和ProcessStartInfor的使用

System.Diagnostics.Process.Start(); 能做什么呢&#xff1f;它主要有以下幾個功能&#xff1a;1、打開某個鏈接網址&#xff08;彈窗&#xff09;。2、定位打開某個文件目錄。3、打開系統特殊文件夾&#xff0c;如“控制面板”等。那么它是怎么實現這幾個功能的呢&#xff1f…

Sublime 的中文亂碼問題

Sublime Text 是現在最受歡迎的文本編輯器&#xff0c;沒有之一。它非常簡潔&#xff0c;而且對各種代碼的高亮顯示很美觀。但是&#xff0c;它默認不支持 GBK、Shift-JIS 等中文、日本編碼格式&#xff0c;故打開此類文件會出現亂碼。 安裝 Package Control 首先要安裝一個包控…

蘋果應用上架遇到的問題(2017年4月27日)

在更新app store的時候報&#xff08;如圖&#xff09;&#xff1a; ERROR ITMS-90086: "Missing 64-bit support. iOS apps submitted to the App Store must include 64-bit support and be built with the iOS 8 SDK or later. We recommend using the default "S…

工作者對象HttpWorkerRequest

在ASP.NET中&#xff0c;用于處理的請求&#xff0c;需要封裝為HttpWorkerRequest類型的對象。該類為抽象類&#xff0c;定義在命名空間System.Web下。 #region Assembly System.Web.dll, v4.0.0.0 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFr…

C#輸入輸出重定向

當 Process 將文本寫入其標準流中時&#xff0c;通常將在控制臺上顯示該文本。通過重定向 StandardOutput 流&#xff0c;可以操作或取消進程的輸出。例如&#xff0c;可以篩選文本、用不同方式將其格式化&#xff0c;也可以將輸出同時寫入控制臺和指定的日志文件中。有兩種方式…

C語言筆試常考知識點

1. const 關鍵字 a) const int a; b) int const a; c) const int *a; d) int * const a; e) int const * const a; 解析&#xff1a; a) a為一個int型變量&#xff0c;在它被定義時就應當對其初始化&#xff0c;因為以后就沒有機會再去改變它了。 b) 與 a) 是一個意思&a…

蘋果應用上架,一些信息的勾選(2017年4月27日)

1、分級的各種選項的選擇全部選否 &#xff08;我們公司是醫療相關的app&#xff0c;醫療的選項也是選擇的否&#xff09; 2、

jsp頁面路徑問題

jsp路徑默認不是項目跟路徑 一、 <% page language"java" import"java.util.*" pageEncoding"utf-8"%> <% String path request.getContextPath(); String basePath request.getScheme() "://" request.getServerName() …

C# 線程池ThreadPool

什么是線程池&#xff1f;為什么要用線程池&#xff1f;怎么用線程池&#xff1f; 1. 什么是線程池&#xff1f;.NET Framework的ThreadPool類提供一個線程池&#xff0c;該線程池可用于執行任務、發送工作項、處理異步 I/O、代表其他線程等待以及處理計時器。那么什么是線程池…

蘋果應用上架,圖片的要求(2017年4月27日)

看這個提示應該就明白了吧。 哈哈&#xff0c;我還是自己再說一遍加深一下印象吧&#xff1a;如果應用在各個尺寸iphone屏幕上面外觀一樣&#xff0c;就只準備5.5英寸的圖就可以了&#xff1b;如果有所不同&#xff0c;就按照實際情況&#xff0c;準備不同屏幕尺寸的圖片即可。…

jQuery操作checkbox

2012歐洲杯"死亡之組"小組出線的國家隊是&#xff1a;<br> <inputtype"checkbox"name"nation"value"Germany">德國 <inputtype"checkbox"name"nation"value"Denmark">丹麥 <input…

android Instrumentation 轉載

Android提供了一系列強大的測試工具&#xff0c;它針對Android的環境&#xff0c;擴展了業內標準的JUnit測試框架。盡管你可以使用JUnit測試Android工程&#xff0c;但Android工具允許你為應用程序的各個方面進行更為復雜的測試&#xff0c;包括單元層面及框架層面。Android測試…

Linker command failed with exit code 1(use -v to see invocation)

Linker command failed with exit code 1(use -v to see invocation) 出現這個問題的原因是&#xff1a;工程當中存在相同的文件&#xff0c;找到該文件將其刪除即可

【C#學習筆記】使用C#中的Dispatcher

form:https://www.jianshu.com/p/0714fc755988之前的文章說過了如何使用BackgroundWorker&#xff0c;今天要說的是WPF程序員處理多線程的另外一個方式 - Dispatcher當我們打開一個WPF應用程序即開啟了一個進程&#xff0c;該進程中至少包含兩個線程。一個線程用于處理呈現&…

流媒體 關鍵詞解釋

流媒體 流媒體是指采用流式傳輸的方式在網上播放的媒體格式, 是邊傳邊播的媒體&#xff0c;是多媒體的一種! 然后就是大家需要了解的幾個關鍵詞 幀:視頻是由很多連續圖像組成, 每一幀就代表一幅靜止的圖像 GOP:&#xff08;Group of Pictures&#xff09;畫面組&#xff0c;一個…

[C#] 等待啟動的進程執行完畢

from: https://www.cnblogs.com/qqhfeng/p/4769524.html有能有時候我們啟動了一個進程&#xff0c;必須等到此進程執行完畢&#xff0c;或是&#xff0c;一段時間&#xff0c; 關閉進程后再繼續往下走。Example sample1 等待應用程序執行完畢 //等待應用程序執行完畢private vo…

html body標簽

table table 屬性&#xff1a; border &#xff1a;定義表格的邊框寬度&#xff0c;默認為0&#xff0c;即無邊框。<table border"1"> title &#xff1a;表格的提示信息&#xff0c;當鼠標移到表格上方時&#xff0c;所提示的信息。 cellpadding &#xff1a;…

創建字符串的方法

//創建一個字符串常量 NSString *str"字符串"; //創建一個空的字符對象 NSString *str1[[NSString alloc]init];//實例方法 NSString *str2[NSString string];//類方法 //快速創建一個字符串 NSString *str3[[NSString alloc]initWithString:"字符串"];//實…

DataReceivedEventHandler 委托 接收調用執行進程返回數據

https://msdn.microsoft.com/zh-cn/library/azure/system.diagnostics.datareceivedeventhandler備注創建 DataReceivedEventHandler 委托時&#xff0c;需要標識將處理該事件的方法。 若要將事件與事件處理程序關聯&#xff0c;請將該委托的一個實例添加到事件中。 除非移除了…