发布

时间:2017-07-20 08:43:44

标签: 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列为5.还在按下" 启动GC之后 "监视器中的按钮,然后再次进行堆转储,它仍然是5.再次播放计数6.为了进行比较,AssetFileDescriptor类的TotalCount在GC之前为5,在GC之后为0。

这5个MediaPlayer个实例(在GC之后)的ReferenceTree看起来与此类似(地址不同):

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按钮无法收集所有内容(至少在第一次运行时)。

2 个答案:

答案 0 :(得分: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!");
}

答案 1 :(得分:0)

请参阅以下链接中的图表: https://developer.android.com/reference/android/media/MediaPlayer.html

在呼叫释放之前,播放器应处于重置状态。

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

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