2012-11-16 34 views
27

我讀過關於呈現用戶界面的碎片的設置.setOnRetainInstance(true)可能導致內存泄漏。用戶界面和內存泄漏的殘留碎片

有人請解釋爲什麼會發生這種情況?我沒有在任何地方找到詳細的解釋。

+0

只是爲了記錄的話題,這裏有類似的主題:http://stackoverflow.com/q/11182180/693752 – Snicolas

+0

HTTP://計算器。 com/q/11160412/693752 – Snicolas

回答

75

在帶有UI的Fragment中,您經常會將一些View作爲實例狀態來加速訪問。例如指向您的EditText的鏈接,因此您不必一直使用findViewById

問題是View保留對Activity上下文的引用。現在,如果您保留View,您還保留對該上下文的引用。

如果上下文仍然有效,但典型的保留情況是重新啓動活動,那麼這沒有問題。例如,經常用於屏幕旋轉。活動娛樂將創建一個新的上下文,舊的上下文旨在被垃圾收集。但現在不能垃圾收集,因爲您的Fragment仍然有一箇舊的參考。

以下示例說明如何不這樣做

public class LeakyFragment extends Fragment { 

    private View mLeak; // retained 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRetainInstance(true); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     mLeak = inflater.inflate(R.layout.whatever, container, false); 
     return mLeak; 
    } 

    @Override 
    public void onDestroyView() { 
     super.onDestroyView(); 
     // not cleaning up. 
    } 
} 

爲了擺脫這個問題,你需要清除onDestroyView你的UI的所有引用。一旦Fragment實例被重新使用,您將被要求在onCreateView上創建一個新的UI。在onDestroyView之後保留UI也沒有意義。 Ui不會被使用。

在這個例子中的修復纔剛剛改變onDestroyView

@Override 
public void onDestroyView() { 
    super.onDestroyView(); 
    mLeak = null; // now cleaning up! 
} 

而除了保持到View S變量,你顯然應該不保持引用到Activity(例如,從onAttach - 乾淨的onDetach)或任何Context(除非它是Application上下文)。

+0

有任何想法,如何處理,這可能會導致空指針山?我有幾個動畫偵聽器,線程,並且每個人都使用其中一個引用,它在onDestroyView中被取消。所以無論何時我使用其中一個引用,我首先必須檢查null。這非常不方便。 – Tamas

+1

@Tamas Listeners,Threads,...都可以保持引用,只要它們不保留對任何引用「Activity」的引用即可。如果他們有類似的引用和活動重新使用它不會導致任何有效的,所以你必須更新它無論如何。 – zapl

+1

@Tamas示例:http://pastebin.com/8A18kMym你基本上需要傳播'onAttach' /'onDetach'到任何引用上下文和'onCreateView' /'onDestroyView'的任何引用視圖的東西。 – zapl

2

setRetainInstance(true)用於在Activity重新創建期間保留動態片段的實例,例如屏幕旋轉或其他配置更改。這並不意味着系統會永遠保留片段。

由於其他原因(例如用戶完成活動(即後退))終止Activity時,Fragment應該有資格進行垃圾回收。

3

保留耦合到Activity的特定對象時要小心。

注意:雖然你可以返回任何對象,你永遠不應該傳遞是聯繫在一起的活動,如可繪製對象,適配器,一個查看或者某個相關的任何其他對象與上下文。如果這樣做,它會泄漏原始活動實例的所有視圖和資源。 (泄漏資源意味着您的應用程序會保留它們,並且它們不能被垃圾收集,因此可能會丟失大量內存。)

http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject

-7

您可以在此改變onDestroy()並調用垃圾收集器。

@Override 
public void onDestroy() { 
    super.onDestroy(); 
    System.gc(); 
    System.gc(); 
} 
+3

有20%的機會可以工作:P 2x'System.gc()'爲了安全嗎?永遠不要依賴'gc()' –

0

「setRetainInstance」用於在活動重新創建時維護片段的狀態。 根據官方文檔:如果我們使用「setRetainInstance」,片段生命週期的兩個方法將不會被執行(onCreate,onDestroy)。 但是,片段中包含的視圖將被重新創建,這是因爲生命週期將從「onCreateView」執行。 在這些情況下,如果我們在「onSaveInstanceState」中保存了一些數據,我們應該在「onActivityCreated」中而不是在「onCreate」中請求它。

公報信息:https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

更多信息:https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en