Android media player retains application instances and causes memory leaks
I have a media player activity as a member variable
My media player is initialized as follows:
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(MyActivity.this, URL);
mMediaPlayer.prepareAsync();
//i set a on Prepared Listener to start playing on Prepared
Everything is normal, and then I overwrite my active onstop method to publish media player
if(mMediaPlayer!=null){
if(mMediaPlayer.isPlaying()){
mMediaPlayer.stop();
}
mMediaPlayer.release();
mMediaPlayer=null;
}
But after the activity stopped, I received a memory leak report from leakcanary
The report reads as follows:
MyApp.Instance->
MyApp.mLoadedApk->
LoadedApk.mReceivers->
ArrayMap.mArray->
arrayObject[].[3]->
ArrayMap.mArray->
arrayObject[][0]->
MediaPlayer.mProxyReceiver->
MediaPlayer.mProxyContext->
leaks MyActivity instance.
MyApp.Instance->
MyApp.mLoadedApk->
LoadedApk.mReceivers->
ArrayMap.mArray->
arrayObject[].[3]->
ArrayMap.mArray->
arrayObject[][0]->
MediaPlayer.mProxyReceiver->
MediaPlayer.mProxyContext->
leaks MyActivity instance.
Yes, I have a myapp class that extends the application and holds a reference to the myapp instance in the static field, but I have never used the reference in the activity. How to solve this leak?
[Edit]
This is my activity Code:
public class PlayActivity extends ActionBarActivity {
private MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
protected void onStop() {
super.onStop();
if(mediaPlayer!=null){
if(mediaPlayer.isPlaying()){
mediaPlayer.stop();
}
mediaPlayer.release();
mediaPlayer=null;
}
}
public void playIt(View view){
if(mediaPlayer==null){
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
if(!mediaPlayer.isPlaying()){
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mediaPlayer.setDataSource(PlayActivity.this, Uri.parse("http://www.noiseaddicts.com/samples_1w72b820/142.mp3"));
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
resolvent:
During the test, I found that this occurred during HTTP streaming
When I check the source code of mediaplayer on the target (api19 in my example), there is indeed a streamproxy instance being set and allocated, but it is not released in the release method, but only in the reset method
This works for me:
if (mediaPlayer != null) {
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}