(Android MediaPlayer)如果MediaPlayer.create()隐式调用prepare(),我该如何调用setAudioStreamType()方法?

12

我正在编写一款Android闹钟应用程序,它使用Service来播放闹铃声音。目前,我能够播放音频,但是可以通过调低设备的音量来将其静音。因此,我尝试添加一个调用setAudioStreamType(AudioManager.STREAM_ALARM);来防止这种情况。

以下是我的服务的onStartCommand()函数:

MediaPlayer mMP;    
@Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        try
        {
            mMP = MediaPlayer.create(this, R.raw.alarm);
            mMP.setAudioStreamType(AudioManager.STREAM_ALARM);
            mMP.setLooping(true);
            //mMP.prepare(); commented out since prepare() is called in create
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        if (mMP != null) mMP.start();

        return START_STICKY;
    }
我的问题是,调用setAudioStreamType()后,MediaPlayer不再播放音频。如果我注释掉那一行,音频就会播放。
加上那一行,我会得到以下运行时错误:
04-10 19:32:03.115: E/MediaPlayer(3411): setAudioStream called in state 8 04-10 19:32:03.115: E/MediaPlayer(3411): error (-38, 0) 04-10 19:32:03.115: E/MediaPlayer(3411): start called in state 0 04-10 19:32:03.115: E/MediaPlayer(3411): error (-38, 0) 04-10 19:32:03.115: E/MediaPlayer(3411): Error (-38,0) 04-10 19:32:03.115: E/MediaPlayer(3411): Error (-38,0)
一些研究(我现在找不到链接了)告诉我,在调用prepare()之后不能调用setAudioStreamType(),而create()隐式地调用prepare()。
无论如何,我应该如何在没有这样的错误的情况下使用setAudioStreamType()呢?
2个回答

13

你可以调用mp.reset(),然后设置流类型、数据源,再调用准备方法。或者直接使用默认构造函数并自己处理初始化。

编辑:

Resources res = getResources();
AssetFileDescriptor afd = res.openRawResourceFd(R.raw.alarm);

mp.reset();
mp.setAudioStreamType(AudioManager.STREAM_ALARM);
mp.setLooping(true);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mp.prepare();
mp.start();

你说得对,那个特定的错误是在调用 start 后出现的,当我发布评论时,在调用 prepare 时我错过了一个错误。 04-10 21:42:11.896: E/MediaPlayer(593): prepareAsync called in state 1. - finiteloop
另外,我现在尝试的唯一区别与上面发布的内容是,prepare() 调用没有注释,并且 create() 后立即调用了 reset() - finiteloop
你是否曾经调用过 setDataSource() 方法?否则它不知道你的文件在哪里。 - Kevin Coppock
1
我目前不是很清楚,在create()中学习数据源时,当我传递R.raw.alarm时。由于调用了reset(),那么现在需要调用setDataSource()吗?如果是这样的话,我该怎样引用项目中res/raw目录下的文件呢? - finiteloop
1
是的,一旦你重置它,它就完全未初始化了,这就是为什么我相当确定你可以只使用默认构造函数(除非在create()方法中有一些基本功能必须初始化,而不仅仅是上面显示的函数),并完全省略对create()的调用。尝试上面的示例,让我知道是否适用于您。 - Kevin Coppock
显示剩余6条评论

3

被接受的答案抛出了一个IllegalStateException异常。这个方法可以正常工作。

MediaPlayer mediaPlayer = new MediaPlayer();

try {
  mediaPlayer.setDataSource(
          this,
          getCustomToneUri()
  );

  mediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);

  mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
      mp.start();
    }
  });

  mediaPlayer.prepareAsync();
} catch (IOException e) {
  e.printStackTrace();
}

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