1. 程式人生 > >Android使用View.post()方法記憶體洩漏

Android使用View.post()方法記憶體洩漏

最近開發中,使用 AsyncTask + ProgressDialog 顯示進度資訊,但在AsyncTask停止,Activity finish 後該Activity的例項始終不會被gc,多次執行程式後,會存在多個activity,造成記憶體洩漏。 下面詳細分析一下:

一份顯示進度條的測試程式碼:

  1. publicclass Main extends Activity {  
  2.     @Override
  3.     protectedvoid onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         TextView tv = new TextView(this);  
  6.         tv.setText("Init State");  
  7.         setContentView(tv);  
  8.         tv.setOnClickListener(new OnClickListener() {  
  9.             @Override
  10.             publicvoid onClick(View v) {  
  11.                 showProgress(Main.this);  
  12.             }  
  13.         });  
  14.     }  
  15.     publicvoid showProgress(final Activity activity) {  
  16.         new AsyncTask<Void, Void, Void>() {  
  17.             ProgressDialog progressDial;  
  18.             protectedvoid onPreExecute() {  
  19.                 progressDial  = new ProgressDialog(activity);  
  20.                 progressDial.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
  21.                 progressDial.show();  
  22.             };  
  23.             @Override
  24.             protected Void doInBackground(Void... params) {  
  25.                 doSomeHeavyWork(progressDial);  
  26.                 returnnull;  
  27.             }  
  28.             protectedvoid onPostExecute(Void result) {  
  29.                 progressDial.dismiss();  
  30.             };  
  31.         }.execute();  
  32.     }  
  33.     void doSomeHeavyWork(ProgressDialog progress) {  
  34.         try {  
  35.             for (int i = 1; i <= 10; ++i) {  
  36.                 progress.setProgress(i);  
  37.                 Thread.sleep(1000);  
  38.             }  
  39.         } catch (Exception e) {  
  40.         }  
  41.     }  
  42. }  

上述程式碼發生記憶體洩漏的地方在 doSomeHeavyWork() 的 progress.setProgress(i); 部分;我們看一下setProgress()的實現,最終會呼叫ProgressBar 類的如下方法:

  1. privatesynchronizedvoid refreshProgress(int id, int progress, boolean fromUser) {  
  2.     if (mUiThreadId == Thread.currentThread().getId()) {  
  3.         doRefreshProgress(id, progress, fromUser, true);  
  4.     } else {  
  5.         RefreshProgressRunnable r;  
  6.         if (mRefreshProgressRunnable != null) {  
  7.             // Use cached RefreshProgressRunnable if available
  8.             r = mRefreshProgressRunnable;  
  9.             // Uncache it
  10.             mRefreshProgressRunnable = null;  
  11.             r.setup(id, progress, fromUser);  
  12.         } else {  
  13.             // Make a new one
  14.             r = new RefreshProgressRunnable(id, progress, fromUser);  
  15.         }  
  16.         post(r);  
  17.     }  
  18. }  
  1. privateclass RefreshProgressRunnable implements Runnable {  
  2.         privateint mId;  
  3.         privateint mProgress;  
  4.         privateboolean mFromUser;  
  5.         RefreshProgressRunnable(int id, int progress, boolean fromUser) {  
  6.             mId = id;  
  7.             mProgress = progress;  
  8.             mFromUser = fromUser;  
  9.         }  
  10.         publicvoid run() {  
  11.             doRefreshProgress(mId, mProgress, mFromUser, true);  
  12.             // Put ourselves back in the cache when we are done
  13.             mRefreshProgressRunnable = this;  
  14.         }  
  15.         publicvoid setup(int id, int progress, boolean fromUser) {  
  16.             mId = id;  
  17.             mProgress = progress;  
  18.             mFromUser = fromUser;  
  19.         }  
  20.     }  

if 語句表明當呼叫的該方法的執行緒是UI執行緒時,則直接執行doRefreshProgress() 方法以重新整理介面;否則,建立一個RefreshProgressRunnable,並通過呼叫View.pos()方法將其插入到UI執行緒的訊息佇列中。 View.post()實現如下:

  1. publicboolean post(Runnable action) {  
  2.     Handler handler;  
  3.     AttachInfo attachInfo = mAttachInfo;  
  4.     if (attachInfo != null) {  
  5.         handler = attachInfo.mHandler;  
  6.     } else {  
  7.         // Assume that post will succeed later
  8.         ViewRootImpl.getRunQueue().post(action);  
  9.         returntrue;  
  10.     }  
  11.     return handler.post(action);  
  12. }  

在post() 函式註釋中,明確寫著:This method can be invoked from outside of the UI thread only when this View is attached to a window.

當ProgressDialog還沒有attach到當前window時(ProgressDialog.show() 方法是非同步執行的),mAttachInfo 值為 null,故而執行 else語句,再看一下getRunQueue()和其post() 方法:

  1. staticfinal ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();  
  2. static RunQueue getRunQueue() {  
  3.      RunQueue rq = sRunQueues.get();  
  4.      if (rq != null) {  
  5.          return rq;  
  6.      }  
  7.      rq = new RunQueue();  
  8.      sRunQueues.set(rq);  
  9.      return rq;  
  10.  }  
  11.  ……  
  12.  staticfinalclass RunQueue {  
  13.      privatefinal

    相關推薦

    Android使用View.post()方法記憶體洩漏

    最近開發中,使用 AsyncTask + ProgressDialog 顯示進度資訊,但在AsyncTask停止,Activity finish 後該Activity的例項始終不會被gc,多次執行程式後,會存在多個activity,造成記憶體洩漏。 下面詳細分析一下:

    記憶體洩漏-非UI執行緒使用View.post()方法

    轉自: 最近開發中,使用 AsyncTask + ProgressDialog 顯示進度資訊,但在AsyncTask停止,Activity finish 後該Activity的例項始終不會被gc,多次執行程式後,會存在多個activity,造成記

    Visual Studio中檢測記憶體洩漏方法

    Visual Studio中檢測記憶體洩漏的方法 #include <iostream> //可以定位到發生記憶體洩露 所在的檔案和具體那一行,用於檢測 malloc 分配的記憶體 #define _CRTDBG_MAP_ALLOC #include <s

    android防記憶體洩漏記憶體優化的方法整理

    記憶體洩漏 一、單利洩漏 存在記憶體洩露問題的一些程式碼片段像下面這樣:  public class Util {              private Context mContext;  

    iOS記憶體洩漏檢測方法

    常見洩漏的點 Retain Cycle,Block強引用 NSTimer釋放不當 第三方提供方法造成的記憶體洩漏 CoreFoundation方式申請的記憶體,忘記釋放     1. Block引用記憶體洩漏問題: [cell setSelectTagCityBl

    JavaScript中4種常見的記憶體洩漏及避免方法

    垃圾回收演算法        常用垃圾回收演算法叫做**標記清除 (Mark-and-sweep) **,演算法由以下幾步組成: 1、垃圾回收器建立了一個“roots”列表。roots 通常是程式碼中全域性變數的引用。JavaScrip

    Handler記憶體洩漏分析與解決方法

    最近整理完Android中訊息機制的知識後,想到Handler記憶體洩漏相關的問題也可以順便整理一下,便有了這篇文章,也方便以後自己查閱 為什麼Handler會造成記憶體洩漏 下面是一段簡單的Handler使用 public class MainActivity extend

    iOS 記憶體洩漏排查方法及原因分析

    級別: ★★☆☆☆ 標籤:「iOS」「記憶體洩漏排查」「Leaks工具」 作者: MrLiuQ 審校: QiShare團隊 本文將從以下兩個層面解決iOS記憶體洩漏問題: 記憶體洩漏排查方法(工具) 記憶體洩漏原因分析(解決方案) 在正式開始前,我們

    【Web效能測試】記憶體洩漏測試方法之chrome記憶體快照

    首先明確一下我們測試的目的:客戶端瀏覽器的js記憶體是否存在洩漏,伺服器端的話可不是這麼測,防止使用者使用時瀏覽器卡頓或崩潰。 F12開啟開發者工具,選中Memory頁簽下的Heap snapshot。 每次我們記錄快照前都要點選那個小垃圾桶,清一下快取,因為我們測記憶體洩漏是看

    排查記憶體洩漏最簡單和直觀的方法

    記憶體洩漏無疑會嚴重影響使用者體驗,一些本應該廢棄的資源和物件無法被釋放,導致手機記憶體的浪費,app使用的卡頓,那麼如何排查記憶體洩漏呢? 當然,首先我門有google的官方文件可以參考: 排查記憶體洩漏官方文件 官方文件(二) 大部分部落格的方法

    java記憶體洩漏的分析方法

    這幾天,一直在為Java的“記憶體洩露”問題糾結。Java應用程式佔用的記憶體在不斷的、有規律的上漲,最終超過了監控閾值。福爾摩 斯不得不出手了!   記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如

    C++ 記憶體洩漏檢測1:微軟自帶的記憶體洩漏檢測方法

    在程式總的包含標頭檔案中新增以下程式碼, #ifdef _DEBUG #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) #else #define DEBUG_CLIENTBLOCK

    C++ 記憶體洩漏檢測方法

    程式中通常包含著靜態儲存區和棧記憶體。靜態儲存區也就是靜態記憶體,是用來儲存區域性static物件、類static資料成員以及定義在任何函式之外的變數(全域性變數)。棧記憶體用來儲存定義在函式內的非static物件。分配在靜態或棧記憶體中的物件由編譯器自動建立

    Android記憶體洩漏場景及解決方法

    本文包括以下內容: 1. 記憶體洩漏原理 2. Android記憶體洩漏發生的情況 3. 檢測記憶體洩漏的工具、方法 4. 如何避免記憶體洩漏 更多Android面試相關請點選 - 四步準備Android面試 - Android開發概要 - 大疆提前批第一次電面

    JavaScript記憶體洩漏的排查方法

    概述 Google Chrome瀏覽器提供了非常強大的JS除錯工具,Heap Profiling便是其中一個。Heap Profiling可以記錄當前的堆記憶體(heap)快照,並生成物件的描述檔案,該描述檔案給出了當時JS執行所用到的所有物件,以及這些物

    JS記憶體洩漏排查方法(Chrome Profiles)

    一、概述 Google Chrome瀏覽器提供了非常強大的JS除錯工具,Heap Profiling便是其中一個。Heap Profiling可以記錄當前的堆記憶體(heap)快照,並生成物件的描述檔案,該描述檔案給出了當時JS執行所用到的所有物件,以及這些物件所佔用的記憶體大小、引用的層級關係等等。這些

    iOS開發使用 runtime 方法中的 class_copyIvarList,class_copyMethodList 方法時導致記憶體洩漏問題

    前段時間在做公司新專案的時候使用了 runtime 的  class_copyIvarList 方法來獲取類的所有屬性的時候,用 leaks 檢測,發現這裡出現了記憶體洩漏。後來查了一些資料發現 class_copyIvarList 返回的物件需要手動釋放。因

    7.JNI 記憶體洩漏 處理 方法總結

    在c++中new的物件,如果不返回java,必須用release掉,否則記憶體洩露。包括NewStringUTF,NewObject。如果返回java不必release,java會自己回收。 jstring jstr = env->NewStringUTF((*p).

    記憶體洩漏的定義及解決方法(智慧指標)

    記憶體洩漏是指由於疏忽或錯誤,程式沒有釋放掉不再使用記憶體的情況。記憶體洩漏並非指記憶體在物理上的消失,而是應用程式分配某段記憶體後,由於設計錯誤,失去了堆該段記憶體的控制,因而造成了記憶體的浪費。 對於C++這種沒有垃圾回收機制的語言來說,主要關注兩種型別的記憶體洩漏:

    簡單記憶體洩漏檢測方法

    我的環境是: XP SP2 . VS2003 最近在一個專案中,程式退出後都出現記憶體洩漏: Detected memory leaks!Dumping objects ->{73} normal block at 0x009C2680, 292 bytes long.