android - MediaPlayer 实例在释放后留在堆中

标签 android

[见下方更新]

我有一个按钮,按下后会播放一​​段短促的声音。声音播放器类如下所示:

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 之后)看起来类似于此(地址不同):

enter image description here

我还尝试了代码的变体,其中 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/

相关文章:

java - 在 Android 应用程序的 Java JSch 中从字符串或资源加载私钥

java - 如何在 android 中获取 EditText 提示的本地化日期格式

Android - IllegalStateException : map has zero size

Android Phone EditText 未更新设备上的显示

android - Admob插屏显示黑屏

android - Gitignore 不忽略某些 project.properties

android - Kotlin 未配置 : Android studio

android - 如何在android中的按钮中存储元数据?

android - Flutter安装更新的apk下载

java - 如何在android中创建字典?