2017-07-20 19 views
2

[請參閱下面的更新]MediaPlayer實例在發佈後留在堆中

我有一個按鈕,可以在按下它之後播放短暫的聲音。聲音播放類看起來是這樣的:

public void play (String filePath) { 
    ... 
    AssetFileDescriptor afd = assetManager.openFd(filePath) 
    play(afd) 
} 

private void play(AssetFileDescriptor afd) { 
     try { 
      MediaPlayer mVoicePlayer = new MediaPlayer(); 
      mVoicePlayer.setOnCompletionListener(this); 
      mVoicePlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
      afd.close(); 
      mVoicePlayer.prepare(); 
      mVoicePlayer.setLooping(false); 
      mVoicePlayer.start(); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

@Override 
    public void onCompletion(MediaPlayer mp) { 
     mp.release(); 
     mp = null; 
     Log.v (TAG, "media released!"); 
    } 

的想法是播放聲音和不再被使用時,它釋放MediaPlayer和自由內存(聲音已播放完畢)。

但播放聲音5次,看着Android的錄音室監聽堆轉儲之後,MediaPlayer類的TOTALCOUNT列5.還壓制後在監視器「啓動GC」按鈕,然後使再次堆轉儲,它仍然是5.再次播放一次計數6.作爲比較,AssetFileDescriptor類的TotalCount在GC之前爲5,在GC之後爲0。

的ReferenceTree對於那些5個MediaPlayer實例(GC後)與此類似(地址不同):

enter image description here

我也嘗試了代碼的變化,其中setOnCompletionListener()創造了一個新的偵聽器內聯,而不是指的是this,但結果是一樣的。

爲什麼這些實例留在內存中以及如何處理?

[更新]

我已經做了幾個測試。首先,它看起來像Android Studio Monitor中的GC按鈕收集未使用的MediaPlayer引用,但僅在第二次運行時收集。即使有300個未使用的實例(聲音被播放了300次),第一次點擊GC按鈕並不會刪除它們,只有第二次點擊會發生。這種效果是可重複的。

在第二次測試中,我一直播放聲音很多次,以致手機發生自動GC(沒有點擊GC按鈕)。它在Android Studio內存監視器藍色圖形中可見:Free Memory達到零後,Free Memory增加了幾MB,同時分配內存下降(自動GC的標誌)。但MediaPlayer實例並未發佈,儘管堆轉儲中已有700多個實例。

在第三次測試中,我一直播放數百次的聲音。在某些時候,我注意到它們在堆轉儲上的數量減少了(但是到了幾百個而不是零)。

所以,

也許這不是我的代碼問題,這是有意設計的自動GC本身決定是否和多少實例,以收集和明確的。由於某種原因,Android Studio中的GC按鈕並沒有收集到所有可能(至少在第一次運行時)。

回答

0

請嘗試像下面一樣採用MediaPlayer的實例變量來做到這一點。

MediaPlayer mVoicePlayer; 
//-------------------- 
public void play (String filePath) { 

    AssetFileDescriptor afd = assetManager.openFd(filePath) 
    play(afd); 
} 

private void play(AssetFileDescriptor afd) { 
    try { 
     mVoicePlayer = new MediaPlayer(); 
     mVoicePlayer.setOnCompletionListener(this); 
     mVoicePlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
     afd.close(); 
     mVoicePlayer.prepare(); 
     mVoicePlayer.setLooping(false); 
     mVoicePlayer.start(); 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
} 

@Override 
public void onCompletion(MediaPlayer mp) { 
    if (mVoicePlayer!=null){ 
     mVoicePlayer.release();  
    } 

    Log.v (TAG, "media released!"); 
} 
+0

我試過你的解決方案,但結果是一樣的;看到我的更新。 – camcam

+0

您確定play()方法在任何情況下都不會被多次調用嗎?因爲如果發生這種情況,那麼您的媒體播放器會創建多個對象。 –