[见下方更新]
我有一个按钮,按下后会播放一段短促的声音。声音播放器类如下所示:
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 Studio Monitor 中的堆转储后,MediaPlayer
的 TotalCount 列class 是 5。同样在按下 Monitor 中的“Initiate GC”按钮然后再次进行堆转储后,它仍然是 5。再玩一次使计数为 6。为了比较,AssetFileDescriptor
的 TotalCount GC 前 class 为 5,GC 后 class 为 0。
那些 5 MediaPlayer
的 ReferenceTree实例(在 GC 之后)看起来类似于此(地址不同):
我还尝试了代码的变体,其中 setOnCompletionListener()
创建了一个新的内联监听器而不是引用 this
, 但结果是一样的。
为什么那些实例会留在内存中,如何处理?
[更新]
我又做了一些测试。首先,看起来 Android Studio Monitor 中的 GC 按钮确实收集了未使用的 MediaPlayer 引用,但仅在其第二次运行时。即使有 300 个未使用的实例(声音播放了 300 次),第一次单击 GC 按钮也不会删除它们,只有第二次单击才会删除。这种效果是可重复的。
在第二次测试中,我一直播放声音很多次,以至于手机上发生了自动 GC(没有点击 GC 按钮)。在 Android Studio 内存监视器蓝图中可见:Free Memory 归零后,Free Memory 增加了几 MB,同时 Allocated Memory 下降(自动 GC 的标志)。但是 MediaPlayer 实例没有被释放,尽管堆转储中已经有 700 多个实例。
在第三次测试中,我继续播放了数百次声音。在某些时候,我注意到它们在堆转储上的数量减少了(但减少到几百而不是零)。
所以,
也许这不是我的代码的问题,这是设计的,自动 GC 本身决定是否收集和清除实例以及收集和清除多少实例。由于某种原因,Android Studio 中的 GC 按钮无法收集所有可能的信息(至少在第一次运行时)。
最佳答案
请尝试使用 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!");
}
关于android - MediaPlayer 实例在释放后留在堆中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45209699/