2012-12-04 15 views
7

我是Android/Java中的新手(通常使用PHP和JavaScript)。我已經閱讀了一些關於應用程序內存泄漏問題的文章,這些文章以錯誤的方式使用引用,所以我有一個關於我在其他人的工作中經常見到的問題的問題。包含引用好/壞的屬性

很多人,當他們需要訪問像視圖和在等多種方法的東西,保持在其中創建活動的過程中分配財產的這個參考。從我讀過的內容(或者至少理解了我讀過的內容),這是內存泄漏的課程之一?

是更好的ID分配給對象,然後在每個方法尋找他們呢?如果是這樣,那麼動態創建的對象呢?

回答

0

在Android中引用的泄漏問題出現在引用了也引用了Context對象的事物時,它們被而非綁定到UI。在其包含活動中持有對視圖的引用,因爲某些時候系統中的任何內容都不會再引用該活動(在onDestroy()之後),並且由於不再從垃圾收集根目錄引用它(如全局變量),它有資格進行垃圾收集。換句話說,一旦活動與其視圖之間建立的循環引用不再重要,否則其他持有對該活動的引用。

問題出在哪裏發送活動的引用 - 本身 a Context - UI和活動生命週期調用之外。像位置偵聽器或某些您忘記註銷的內容。這將持有該參考使整個樹不適合垃圾收集。因此,大泄漏。

2

如果你正確使用它們,那麼很好,如果你不正確使用它們。

如果傳遞活動之外的東西到另一個階級和階級的生活時間比活動的續航時間大於你只泄漏。 Android可能會在您的活動不再是前臺活動時銷燬活動,並且如果活動外的某些內容持有對該活動的引用,那麼垃圾收集器無法將內存釋放回堆中。

要與活動環境中,靜態和單身特別小心。

只需保持參考到活動內部的視圖是完全正常。

下面是壞(僞碼)的一個例子;

public class MyApplication extends Application{ 
    public static ImageView activityBackgroundImageView; 
} 

public class MyActivity extends Activity{ 

    ImageView iv = findViewById(R.id.myImageView); 
    myApplication.activityBackgroundImageView= iv; // <==== LEAK 

} 

實際上,泄漏並不存在,它只在(如果)myActivity完成()或銷燬時泄漏。

每個對象都有一個引用計數 - 有多少對象保持對它的引用。在您的活動中設置對ImageView的引用後,引用計數爲1。然後,您將該引用複製到Application類。 NB。 Java中的所有東西都是按值傳遞的,因此您可以傳遞引用的值,即值的副本 - 即對同一對象的新引用。 ImageView上的引用計數現在是兩個。

一段時間後,你完成()的活動,並引用計數遞減。現在是一個。垃圾收集器不能釋放該ImageView對象,因爲它具有非零引用計數。

當然,您可以通過將應用程序中的引用置零來修復它,但您現在已經有意大利細麪條代碼。

+1

對圖像的靜態引用特別是一個問題,因爲它們自身顯然以某種方式綁定到活動。所以對位圖對象的靜態引用將意味着該活動永遠不會被釋放。 – Emile

+0

是的,和任何人誰是困惑,我原來的答覆使用一個靜態的位圖這是我後來簡化爲參照,將泄漏的整個活動,因爲它有到它實例化活動的背景下,參考了ImageView的。 – Simon

0

你對內存泄漏原因的觀察並不完全正確。

存儲對視圖元素的引用是非常好的,它是如何做到這一點,並使用它們可能導致內存泄漏。例如,避免使用靜態引用,例如,如果靜態引用位圖圖像,您可能會無意中導致垃圾回收問題,正如Simon在答案中指出的那樣。

所以它很好做以下事情。

class{ 
private TextView myTextView; 

onCreate() 
myTextView = findViewById(R.id.mytextview); 

myMethod() 
myTextView.text = "hello view." 

} 

的myMethod的使用現有的基準純粹是爲了方便,沒有什麼阻止你把

findViewById(R.id.mytextview).text = "hello view"; 

然而,這將使爲真正不可讀的代碼,如果你有很多的參考。所以你可以使用一個局部範圍變量。

myMethod() 
TextView myTextView = findViewById(R.id.mytextview); 
myTextView.text = "Hello" 
..... 

根據您的個人喜好,它不一定會導致內存泄漏。

現在這裏的問題是,findViewById是一個密集的過程,所以你真的不想重複調用。列表視圖特別容易出現這種情況,如果您不適應這種情況,將顯着減慢速度。

因此,在列表視圖中,您會發現人們實現了viewHolder模式。一個小對象,您可以將視圖子元素的引用分配給。然後將該對象分配給父視圖標籤屬性。在後續對視圖的調用期間,您將測試視圖標籤屬性是否具有viewHolder,如果它包含,則會引用子對象,這樣每次需要更新視圖內容時都可以節省調用findViewById的時間和精力。

非常粗略的想法,其實施方式略有不同。 viewHolder = new ViewHolder(); viewHolder.myTextField = findViewById(R.id.mytextview); myView.setTag(viewHolder) .... 如果(viewHolder) viewHolder.text = 「你好」

請注意,您只能在列表視圖中使用。我沒有把它用作一般的經驗法則。

爲列表視圖適配器查找高效的視圖持有者模式。

0

感謝您的答案,只是想確保在創建一個應用程序,會產生問題的人。

作爲一項安全措施,我添加了onStop和onRestart以刪除和重新創建參考。這應該確保在應用程序處於後臺時不存在這種類型,儘管我從不在靜態屬性中使用引用。