【问题标题】:Mediaplayer Cutting Off Sound Too Soon媒体播放器过早切断声音
【发布时间】:2012-05-29 02:47:29
【问题描述】:

我可以在两种设备上测试我的游戏:美国 Celluar 手机 (SCH-R880) 和 Kindle Fire,Kindle Fire 比手机强大得多。

我有几个短的(少于或大约 1 秒)音效。为了节省内存,我加载、播放和释放其中一些音效。在电话上,他们(大部分)按预期播放。然而,在 Kindle Fire 上,它们被缩短了。真的很短的声音很快就被切断了,我什么也听不见。但是,在设置时加载并保留的那些可以正常播放。

有人知道这里发生了什么吗?我是否以某种方式过早地发布了我的媒体?下面是一个例子。在电话里,我听到“第二级!”但在 Kindle 上,我听到类似“Lev tw”的声音。

mpNum = null;

try
{
    switch (level)
    {
        case 2:
            mpNum = MediaPlayer.create(contxt, R.raw.l2); break;
        case 3:
            mpNum = MediaPlayer.create(contxt, R.raw.l3); break;
        case 4:
            mpNum = MediaPlayer.create(contxt, R.raw.l4); break;
        case 5:
            mpNum = MediaPlayer.create(contxt, R.raw.l5); break;
        case 6:
            mpNum = MediaPlayer.create(contxt, R.raw.l6); break;
        case 7:
            mpNum = MediaPlayer.create(contxt, R.raw.l7); break;
        case 8:
            mpNum = MediaPlayer.create(contxt, R.raw.l8); break;
        default:
            return;
    }

    MediaPlayer vLevel = MediaPlayer.create(contxt, R.raw.level);

    vLevel.setOnPreparedListener(new MediaPlayer.OnPreparedListener()
    {
        public void onPrepared(MediaPlayer mp)
        {
            mp.start();
        }
    });

    vLevel.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
    {
           public void onCompletion(MediaPlayer mpl)
           {
            mpNum.start();
            mpl.release();
           }
       });

        mpNum.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
        {
            public void onCompletion(MediaPlayer mp)
            {
                mp.release();
            }
        });
    }
    catch (Exception e) {}

为了尝试解决此问题,我尝试了 SoundPool,但它不起作用;我什么也没听到。下面是我尝试使用 SoundPool 播放音乐时所做的:

SoundPool soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 100);
    soundPool.load(contxt, R.raw.song2, 1);
    AudioManager mgr = (AudioManager)contxt.getSystemService(Context.AUDIO_SERVICE);
    float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
    float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
    float volume =  streamVolumeMax;  

    soundPool.play(1, volume, volume, 1, 5, 2);

更新

我注意到当一个应该播放(但没有)的声音出现这个错误时:

AudioPolicyManager: stopOutput() oldDevice 2
AudioPolicyManager: [getDeviceForStrategy] strategy : 0,forceUse(0)

【问题讨论】:

  • 我在 Nexus 5 设备上注意到了这个问题。基本上我的应用程序流式传输音频和 MediaPlayer 正在削减大约。 1-5 秒太早了。还有人注意到这一点吗?
  • 我正在开发的应用程序也有这个问题,这让我发疯,因为我无法用我现有的手机重现它,但它似乎在 Galaxy S4 上失败了。似乎这个 bug 是在 kitkat 中引入的,只有在非常特殊的情况下才会弹出。

标签: android android-mediaplayer


【解决方案1】:

我遇到了同样的问题,确实是垃圾收集器问题!如果将媒体播放器的实例分配给方法内部的某个局部变量,则离开该方法后该实例不再存在,因此 GC 可以随时随机删除它。

为避免这种情况,您需要在其他地方至少保留一个指向该实例的指针。我创建了一个静态集来保留所有指针,它看起来像这样:

private static Set<MediaPlayer> activePlayers = new HashSet<MediaPlayer>();

然后媒体播放器的创建如下:

MediaPlayer player = MediaPlayer.create(context, context.getResources().getIdentifier(id, "raw", pkg));
activePlayers.add(player);
player.setOnCompletionListener(releaseOnFinishListener);
player.start();

其中 releaseOnFinishListener 是一个侦听器,它像您的一样在完成时释放媒体播放器,但也会从 activePlayers 集中删除指针:

MediaPlayer.OnCompletionListener releaseOnFinishListener = new MediaPlayer.OnCompletionListener() {
    public void onCompletion(MediaPlayer mp) {
        mp.release();
        activePlayers.remove(mp);
    }
};

希望对你有帮助。

【讨论】:

  • 有效。谢谢你:)
  • 确实是GC问题!就我而言,在全球范围内声明 MediaPlayer 列表可以完成这项工作。谢谢~
  • 在以后的Android Studio版本中Profiler可以用来强制GC,这使得这个场景很容易测试。
  • 你先生,应该获得奖章。
【解决方案2】:

好吧,我在一个项目中遇到了同样的问题,因为在 KindleFire 中,声音在完成之前就被切断了,所以为了解决这个问题(随着发布),我在发布中添加了延迟:

    mpNum.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
    {
        public void onCompletion(MediaPlayer mp)
        {
            // Add a handler delay
            new Timer().schedule(new TimerTask() {

                @Override
                public void run() {
                   mp.release();
               }
            }, DELAY_TIME);
        }
    });

这不是最好的解决方案,但我认为这可能会对您有所帮助。

【讨论】:

  • 我认为这也是问题,但我删除了我的 release() 代码,它仍然发生。
【解决方案3】:

我听说 SoundPool 类比 MediaPlayer 更适合像这样的短声音,但这可能不是问题。

您还可以发布您的点击处理程序的代码吗?

另外,我建议不要释放和重新获得 MediaPlayer 实例,而是使用一个 MediaPlayer 实例,并在需要重用它时遵循典型的 reset()、setDataSource()、prepare()、start() 序列。它会比每次都构建一个新实例更有效。即:

MediaPlayer mp = new MediaPlayer();
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.sound1);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength();
mp.prepare();
mp.start();

//to reuse
mp.reset();
afd = getResources().openRawResourceFd(R.raw.sound2);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength();
mp.prepare();
mp.start();

【讨论】:

  • 是的,你的 onClick() 方法……至少我假设你是这样触发这些音效的?基本上就是你用来触发声音播放的任何东西。您上面的代码都没有这样做。
  • 其实没有。这是一个游戏,所以这些声音效果是根据特定事件调用的。我可以给你看,但相信我,它不会有帮助。显示方法调用本身无济于事,要提供更好的图片需要显示大量代码。
  • 更新帖子。知道那个错误是什么吗?似乎无法通过谷歌找到它。
  • 这对我不起作用,在调用 reset() 时会抛出 IllegalStateException
  • SoundPool 示例的链接无效。你能更新一下吗?谢谢
【解决方案4】:

我遇到了这个问题并解决了它,将我的 MediaPlayer 对象设为私有,因此垃圾收集器不会清理实例。希望它可以帮助别人!

【讨论】:

  • 我不确定,但声音已停止被剪切。
  • 这不是 GC 的工作方式。可能是相关而不是因果关系。
【解决方案5】:

这很奇怪,而且不是一个好的解决方法,但我目前发现的唯一解决方法。如果我删除 mp.setOnPreparedListenermp.setOnCompletionListener 并只保留 mp.start(),它可以工作。

【讨论】:

  • 这对我也不起作用(在 Android 4.1 和 6.0.1 上测试)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多