无法触发android MediaPlayer的onCompletion事件

15

我正在尝试使用Android的MediaPlayer类播放一些声音。

这是代码:

MediaPlayer mp = new MediaPlayer(); 
mp.setDataSource(context, Uri.parse(soundUrl)); 
mp.setAudioStreamType(AudioManager.STREAM_MUSIC); 
mp.setLooping(false); 
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 
    @Override 
            public void onCompletion(MediaPlayer mp) { 
                Log.i(LOGTAG, "onComplete hit"); 
                mp.stop(); 
                mp.release(); 
            } 
    });         

mp.prepare(); 
mp.start();

这段代码在一个服务中运行,但是由于某些原因,在onCompletion中设置的任何内容似乎都不会触发。之后我在logcat中收到一条消息,说mediaplayer没有被释放。我不知道我做错了什么。

我在Galaxy Nexus 4.0.4的原生ROM上进行测试。

我还注意到声音可能在结束时被裁剪。


也许该应用程序正在休眠。请尝试此链接:https://dev59.com/qFPTa4cB1Zd3GeqPmMSb#60911323 - SJX
5个回答

35

实际上很简单(但有点傻)。只需要在调用 start() 后设置你的 listener,像这样:

ediaPlayer mp = new MediaPlayer(); 
mp.setDataSource(context, Uri.parse(soundUrl)); 
mp.setAudioStreamType(AudioManager.STREAM_MUSIC); 
mp.setLooping(false); 
mp.prepare(); 
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 
    @Override 
            public void onCompletion(MediaPlayer mp) { 
                Log.i(LOGTAG, "onComplete hit"); 
                mp.stop(); 
                mp.release(); 
            } 
    });         

6
在设置完成监听器之前调用 start() 的重要性是什么?这是一个 Android 的怪癖吗? - Fabian Tamp
1
@FabianTamp 这是另一个 Android 的怪癖。可能并不是所有设备都需要(甚至可能已经不需要了),但当时它绝对是解决问题的方法。 - ajacian81
2
我没有看到任何支持在Android源代码中修复问题的证据。事实证明,对我来说根本问题是MediaPlayer在播放结束之前被GC处理了,我已经添加了一个更详细的答案来概述这个问题 :) - Fabian Tamp
我再也抱怨不了了,这个救了我 :) - Ceddy Muhoza

3

这是我的设置:

    video.setOnCompletionListener(this);
    IntroClip.execute(video);
}

@Override
public void onCompletion(MediaPlayer mp){
    Button LoginButton;
    Button CreateAccount;
    Button RecoverPass;

    setContentView(R.layout.loginmenu);
    Spin = (ProgressBar)findViewById(R.id.Spinner);

    mp.release();       
}

我猜“video”是MediaPlayer的实例?而且你的类中有“implements MediaPlayer.OnCompletionListener”吗?我也尝试过这种方法,但它没有起作用。 - Andrew
2
@Andrew 如果它没有起作用,那么它怎么成为被接受的答案了呢?你让它起作用了吗?如果是的话,怎么做到的? - hendrix
2
在设置监听器之前,请调用start()函数,@smitalm。 - ajacian81

2
我遇到了类似的症状,根本原因是在调用 OnCompletionListener 之前,MediaPlayer 已被垃圾回收了。
从你的代码来看,似乎也是同样的问题 - 你的代码没有持有 MediaPlayer 的长期引用,因此一旦该函数结束(并在音频播放完成之前),MediaPlayer 就容易受到 GC 的影响。
这个问题可以通过以下日志行进行识别:
02-22 13:14:57.969: W/MediaPlayer-JNI(16888): MediaPlayer finalized without being released

您可以通过重新设计类,使 MediaPlayer 引用保持更长时间来解决此问题 - 例如,在活动中存储对其的引用,并重复使用相同的实例多次播放相同的声音。

这里有更详细的解释:Garbage Collection causes : MediaPlayer finalized without being released


2

实际上,问题出在MediaPlayer是一个局部变量。函数执行完毕后,MediaPlayer被垃圾回收器回收了。

因此,解决方法很简单,将你的MediaPlayer作为类的成员变量即可。

YourClassName {
    MediaPlayer mp = new MediaPlayer(); 

    void YourFunction() {
          mp.setDataSource(context, Uri.parse(soundUrl)); 
          mp.setAudioStreamType(AudioManager.STREAM_MUSIC); 
          mp.setLooping(false); 
          mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 
              @Override 
              public void onCompletion(MediaPlayer mp) { 
                  Log.i(LOGTAG, "onComplete hit"); 
                  mp.stop(); 
                  mp.release(); 
              }
          });         
          mp.prepare(); 
          mp.start();
    }
}

1

有两种方法来初始化MediaPlayer对象,即“new”和“create()”。为了执行OnCompletionListener,对于这两种方法获取的对象是不同的。

1)“new”方法

MediaPlayer mp = new MediaPlayer();
mp.setDataSource(context, Uri.parse(soundUrl)); 
mp.setAudioStreamType(AudioManager.STREAM_MUSIC); 
mp.setLooping(false); 
mp.prepare(); 
mp.start();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { 
    @Override 
    public void onCompletion(MediaPlayer mp) { 
        Log.i(LOGTAG, "onComplete hit"); 
        mp.stop(); 
        mp.release(); 
    } 
}); 

2)"create"方法
MediaPlayer mp = MediaPlayer.create(getActivity(), Uri.parse(soundUrl));
//mp.prepare() is not needed here
mp.setLooping(false);
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
    @Override
    public void onCompletion(MediaPlayer mp) {
        Log.i(LOGTAG, "onComplete hit");
        mp.stop();
        mp.release();
    }
});

对于create()方法,我也遇到了类似的问题。如果在调用create()之后调用mp.prepare(),则该过程将永远不会到达以下的setOnCompletionListener,甚至不会到达start()。其根本原因是“如果使用create方法创建成功,则对象处于准备状态”(https://developer.android.com/reference/android/media/MediaPlayer.html)。因此,在使用create()方法后,您不需要调用prepare()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接