2015-06-24 41 views
13

我在Activity類初始化一個成員變量活動的成員範圍和的AsyncTask

private String test = new String("A"); 

然後我用它來寫的很長一段時間在一個匿名的AsyncTask的doInBackground()方法耗時循環記錄從活動

推出
new AsyncTask<Void, Void, Void>() { 
    @Override 
    protected void onPreExecute() { 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 

    for (int j = 10; j >= 0; j--) { 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     Log.i("DOINBACKGROUND ", test); 
    } 

}.execute(); 

問題: 後,當我離開的活動,同時Asynctask仍在執行,並活動的onDestroy()執行,我在Log中看到成員變量仍然存在並且沒有被銷燬。有人可以向我解釋怎麼可能?

BOUNTY問題: 成員變量還活着,因爲即使onDestroy()後,它不是尚未garbaged由於GC標準和GC優先。還行吧。

但我的疑問是,如果

  • 「測試」成員變量(和活動的範圍內)將不會garbaged直到引用的AsyncTask結束了它的東西,這樣的AsyncTask可以隨時且確實地完成其doInBackground()沒有崩潰(儘管具有臨時存儲器消耗)

或代替

  • '測試' 成員變量將b Ëgarbaged遲早不管的AsyncTask運行,可能導致一個asysnctask的崩潰

回答

8

不要混淆垃圾收集和活動生命週期。

一旦從GC根對象追蹤到的所有引用都消失,就可以垃圾收集對象。

onDestroy()是活動週期的一部分。本質上,該框架是通過該活動完成的,並且放棄了它可能持有的對活動和相關資源的引用。

當您實例化一個匿名內部類時,它將獲得對父對象的隱式引用。換句話說,anon內部類總是一個非靜態的內部類。此家長參考是對您的Activity的參考。然後,通過調用​​將異步任務對象傳遞給執行程序,並且只要需要,執行程序就會保存異步任務引用,同時也阻止了引用的活動被垃圾收集。


因此,在我的代碼片段的例子永遠和肯定完成其doInBackground()不因NPE崩潰的AsyncTask?

是的。但要考慮以下幾點:

  • 使你的內部類static,除非他們特別需要訪問父對象。由於匿名內部類別始終爲非static,因此請將其設爲非匿名。

  • 混合在具有分離的生命週期的對象(如活動或片段)異步操作是脆弱的,它被更好地避免。問題包括取消,結果傳遞到一個消失的對象,並保持GC防止引用昂貴的對象,如活動。

+0

因此,我的片段示例中的asynctask將始終完成它的doInBackground(),並且肯定不會由於NPE而崩潰? – GPack

3

首先的onDestroy()只可能出現破壞活動之前和詢問活動管理員釋放依賴於該活動的所有資源。這意味着所有活動的資源都將被gc刪除。但是,它不會強制gc從內存中刪除資源,它們只是候選資源。這些候選人將由gc根據他們的規模,年齡,新近程度,類型等進行評分,並且只要系統需要更多空間,就會要求gc移除候選人,並根據他們的分數進行評分。具有較高分數的候選人最有可能首先被刪除。

即使從應用程序中退出後,如果發現崩潰崩潰,這一點很明顯。

如果您創建另一個活動並在其上調用System.gc(),您可能會看到此崩潰。

乾杯 A.

+0

很清楚。我現在懷疑的是,在這種情況下,如果成員變量僅僅因爲gc優先級而仍然存在,但無論asynctask正在運行,它遲早都會被垃圾回收,或者相反,只有當asynctask(保留參考成員變量?)結束了它的工作。 – GPack

+0

asyncktask對象實例屬於該活動並且該活動被銷燬。所以gc已經把所有的變量標記爲垃圾。它們將從內存中刪除。如果你想運行一個長期的任務,使用其他結構,如服務或intentservices,取決於你的情況。 如果您想堅持使用當前的解決方案,爲了防止後臺崩潰,您可以在asynktask對象內部定義變量,然後在onPostExecute()中檢查針對null的回調。通過這樣做,它不會崩潰,但結果仍然會丟失。 –

+0

是的,我的疑問是,這個匿名的asynctask保留了對'測試'外部類'變量的引用,因此對於活動的上下文也是如此。因此,他們,因爲引用,不會垃圾,直到asynctask結束它的東西。我錯了嗎? – GPack

2

一個的AsyncTask是不依賴於包含它的活動的生命週期。因此,例如,如果您在Activity內啓動AsyncTask,並且用戶旋轉設備,則該Activity將被銷燬(並且將創建一個新的Activity實例),但AsyncTask不會死亡,而是繼續活動直至完成。然後,當AsyncTask完成時,不是更新新Activity的UI,而是更新Activity的前一個實例(即創建它的那個實例,但不再顯示!)。這可能導致一個異常(類型爲java.lang。IllegalArgumentException:如果您使用findViewById檢索Activity中的視圖,則視圖不附加到窗口管理器)。

還有可能導致內存泄漏,因爲AsyncTask維護對Activty的引用,只要AsyncTask保持活動狀態,就可以防止Activity被垃圾回收。

由於這些原因,使用AsyncTasks對於長時間運行的後臺任務通常是一個壞主意。相反,對於長時間運行的後臺任務,應該採用不同的機制(如服務)。

+0

我只是爲了能夠在現場驗證一些關於參考的理論問題而進行的嘗試。在這方面,例如,我試圖使用Asysnctask中的findViewById來檢索和更新Activity的視圖,而Activity不再可見(因此在按BACK或更改方向後)。那麼結果是Asynctask結束了它的工作,視圖的更新丟失了,但是我沒有收到任何異常,也沒有在doInBackground()中使用findViewById拋出runOnUiThread()語句,也沒有在OnPostExecute()方法中使用findViewById。 – GPack

3

成員變量test不會被垃圾收集器回收,直到活動的實例是垃圾收集。

活動實例將直到因爲該的AsyncTask被保持到活動實例的引用的的AsyncTask結束不會被垃圾收集器回收。

AsyncTask實例在其完成工作之前不會被垃圾收集。

的AsyncTask將完成doInBackground()方法沒有崩潰。當然。

+0

非常感謝,對參考鏈非常清晰的解釋。 – GPack

0

我猜異步任務也不是一成不變的,它持有參考封閉活動,防止活動進行垃圾回收。

如果你想知道類是如何匿名會導致活動泄漏是指本 -

您也可以嘗試旋轉設備多臺設備,並檢查是否存在由使用以下命令同一活動的多個實例,並檢查沒有活動。

亞行外殼dumpsys meminfo中your.app.packagename

Applications Memory Usage (kB): 
Uptime: 40343748 Realtime: 164852669 

** MEMINFO in pid 16561 [samsung.svl.com.graph] ** 
        Pss Private Private Swapped  Heap  Heap  Heap 
       Total Dirty Clean Dirty  Size Alloc  Free 
       ------ ------ ------ ------ ------ ------ ------ 
    Native Heap  5708  5664  16  2380 20480  8849 11630 
    Dalvik Heap  1163  972  136 27080 37459 29598  7861 
Dalvik Other  604  604  0  4       
     Stack  288  288  0  0       
    Other dev  4  0  4  0       
    .so mmap  3569  992  72  2120       
    .apk mmap  39  0  0  0       
    .ttf mmap  0  0  0  0       
    .oat mmap  539  0  4  0       
    .art mmap  747  524  4  704       
    Other mmap  5  4  0  0       
    GL mtrack 10951 10951  0  0       
     Unknown  2260  2260  0  92       
     TOTAL 25877 22259  236 32380 57939 38447 19491 

Objects 
       Views:  17   ViewRootImpl:  1 
     AppContexts:  3   **Activities:  1** 
       Assets:  3  AssetManagers:  3 
     Local Binders:  8  Proxy Binders:  23 
     Parcel memory:  3   Parcel count:  12 
    Death Recipients:  0  OpenSSL Sockets:  0 

SQL 
     MEMORY_USED:  0 
    PAGECACHE_OVERFLOW:  0   MALLOC_SIZE:  0