【问题标题】:Is my code sufficient to prevent MediaPlayer leakage?我的代码是否足以防止 MediaPlayer 泄漏?
【发布时间】:2012-11-27 08:20:53
【问题描述】:

在我的活动中,我有以下内容:

private Set<MediaPlayer> mediaPlayers;

public void onSomeEventInMyActivity()
{
    // play sound
    MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.my_sound);
    mediaPlayers.add(mediaPlayer);
    mediaPlayer.setOnCompletionListener(new OnCompletionListener()
    {
        @Override
        public void onCompletion(MediaPlayer mp)
        {
            mp.release();
            mediaPlayers.remove(mp);
        }
    });
    mediaPlayer.start();
}

@Override
protected void onStart()
{
    super.onStart();

    mediaPlayers = new HashSet<MediaPlayer>();
}

@Override
protected void onStop()
{
    super.onStop();

    for (MediaPlayer mediaPlayer : mediaPlayers)
    {
        if (mediaPlayer.isPlaying())
        {
            mediaPlayer.stop();
        }
        mediaPlayer.release();
    }
}

此代码是否足够或会导致 MediaPlayer 泄漏?我的onStoponStart 的实现是必要的,还是我可以仅仅依靠在onCompletion 中调用release

我这样编写代码是因为我假设可以在 MediaPlayer 播放时调用onStop(),所以我需要调用release,因为还不会调用onCompletion。我只是猜测这是对的,如果我错了,请纠正我。

我还读到onStop 在内存不足的情况下不会被调用 - 那该怎么办?

【问题讨论】:

    标签: android media-player android-activity android-mediaplayer activity-lifecycle


    【解决方案1】:

    如果 mediaPlayer 预计在活动变得不可见时停止,则需要 onStop() 例程。否则, mediaPlayer 会继续播放。在较旧的操作系统、Gingerbread 和更早的版本上,活动可以执行onPause() - 比如说,当电话到达时 - 并且在极端情况下,可以在不执行 onStop() 的情况下被销毁。我不知道正在运行的 mediaPlayer 会发生什么。但是,如果有电话打进来,最好停止onPause() 中的媒体播放器!后来的操作系统在销毁 Activity 之前总是通过onStop()。停止 mediaPlayer 后在 onPause()onStop() 中调用 mp.release() 是正确的。

    删除mediaPlayers 中持有的玩家的引用也是可取的,这在上面的 onStop() 中不会发生。比如:

        @Override public void onCompletion(MediaPlayer mp) {
            mp.stop();   // It's always safe to call stop()
            mp.release();  // release resources internal to the MediaPlayer
            mediaPlayers.remove(mp); // remove reference to MediaPlayer to allow GC
        }
    

    然后

    @Override public void onPause() {
         for (Object mediaPlayer : mediaPlayers.toArray()) {
              onCompletion((MediaPlayer) mediaPlayer); // stop, release, and free for GC, each mp.
         } 
         super.onPause();
    }
    

    (我最初在上面的代码中有for (Object mediaPlayer : mediaPlayers) {},但 omfeddf345mnof32nisd45fgoq2t 指出我会在迭代时修改一个集合。感谢您的更正!)

    【讨论】:

    • 所以我猜onPause 是最安全的选择,并且您在上面发布的代码应该足以适用于所有新旧操作系统?只是想确认除了您在上面发布的代码之外我不需要做任何其他事情......
    • 我认为不需要任何额外的东西,我就是这样编程的。如果您的应用程序启用了旋转,那么您的活动将被销毁,包括在旋转事件上执行onPause,然后重新创建,您必须在onPause 中注意这一点,而不是在onCompletion 中调用这种情况下,如果您想保持音频继续播放。最简单的禁止旋转!祝你好运!
    • 另一个小问题......创建 MediaPlayer 对象的 HashSet 的最佳位置在哪里? onCreate, onStart, onResume?我只需要做一次,对吧?那么还不如在onCreate...?
    • 嗯,这完全取决于你想对它们做什么......例如,如果手机在播放时进入睡眠状态并再次唤醒,你希望它们发生什么。我建议你再问一个问题,以便详细说明。
    • 嘿,我刚刚注意到你的代码会在onPause 中抛出一个ConcurrentModificationException,因为你在迭代它的同时从集合中删除了MediaPlayers。为了解决这个问题,我让它遍历集合的数组副本,而不是例如for (Object mediaPlayer : activeMediaPlayers.toArray()) { onCompletion((MediaPlayer) mediaPlayer); } 如果您可以用这个(或您认为更好的其他解决方案)更新您的答案,我们将不胜感激。
    【解决方案2】:

    只有保证被调用的回调是onPause(),所以在某些情况下你可能会泄漏这个媒体播放器。如果在活动暂停时停止播放器是不可接受的,您应该使用服务,并注意某些事件(如来电等)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多