无法让Android MediaPlayer onCompletion触发

问题描述:

我想使用Android MediaPlayer类播放一些声音。无法让Android MediaPlayer onCompletion触发

下面是此代码在服务运行的代码

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 stock ROM上运行这个测试。

我还注意到声音最后可能会被截断。

这是我如何把它:

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();  
} 
+0

我猜“视频”是MediaPlayer的一个实例吗?并且你的课堂上有一个“实现MediaPlayer.OnCompletionListener”?我也试过这种方法,但它没有奏效。 – Andrew 2012-04-04 06:06:34

+2

@Andrew它如何被接受的答案,如果它没有工作?你做到了吗?如果是,如何? – hendrix 2013-04-04 15:40:06

+2

@smitalm在设置侦听器之前调用start()。 – ajacian81 2013-10-24 01:50:41

这其实很简单(但愚蠢的)。你调用启动(在设置你的听众),像这样:

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(); 
      } 
    });   
+3

你能解释为什么在设置完成监听器之前调用start()很重要吗?这是Android的怪癖吗? – 2015-06-02 04:58:23

+0

@FabianTamp这是另一个Android怪癖。它可能不是所有设备都需要的(或者甚至不再需要),但当时它绝对是解决问题的方法。 – ajacian81 2015-06-03 15:30:22

+1

虽然我看不到任何证据支持为什么会修复Android源代码中的内容。对我来说,根本问题是MediaPlayer在播放前已经GC'd,我已经添加了一个更详细的答案:) – 2015-06-04 01:04:12

我遇到类似的症状此,根本原因是,MediaPlayer居然也得到了OnCompletionListener之前收集的垃圾是被调用。

从你的代码判断,它看起来像是同样的问题 - 你的代码没有对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

有初始化的MediaPlayer对象两种方法,“新”和“创建()”。为了执行OnCompletionListener,它在这两种方法中获得的对象是不同的。

1)中的 “新” 办法

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) “创造” 的方法

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()。

其实,原因是MediaPlayer是一个局部变量。该功能完成后,MediaPlayer将由GC收集。 所以修复很简单,让您的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(); 
    } 
}