短小的音效很时候放在Android应用程序从操作系统分配到的堆内存中,而包含较长音乐文件的大音频文件就很不适合了。为此,我们就需要将音乐以流的方式输出到音频硬件上,这就意味着每次我们只能读入一小块数据,该数据足于解码成原生的PCM数据并输出到音频芯片上。
这听起来挺吓人的。不过幸运的是,我们有MediaPlayer类,它能处理的所有事情。初始化MediaPlayer类:
MediaPlayer mediaPlayer = new MediaPlayer();
接下来我们需要告诉MediaPlayer播放什么文件,这同样通过AssetFileDescriptor来实现:
AssetFileDescriptor descriptor = assetManager.openFd("music.ogg");
mediaPlayer.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength());
这里稍微比SoundPool中复杂一点。MediaPlayer.setDataSource()方法并没有直接获取AssetFileDescriptor,而是使用一个FileDescriptor,通过AssetFileDescriptor().getFileDescriptor()方法获取该描述符,除此之外我们还需要指定音频文件的偏移量和长度。为什么是偏移量?实际上资源是以单个文件形式存储的,为了让MediaPlayer获得文件的起始地址,我们需要将asset文件夹中的文件的偏移量提供给它。
在播放该音乐文件之前,需要再调用一个方法为MediaPlayer的播放做准备:
MediaPlayer.prepare();
这将实际地打开文件,检查它是否可以读取并用MediaPlayer实例来进行播放。从这里开始,我们就可以随意地播放、暂停和停止音频文件,也可以设置循环播放和改变音量。
启动播放可通过调用下面的方法进行:
mediaPlayer.start();
注意,该方法必须在成功调用MediaPlayer.prepare()方法之后才能调用(你将注意到它是否会抛出一个运行时异常)。
开始播放后,我们可以通过调用pause()方法来暂停播放:
MediaPlayer.pause();
只有我们成功准备好MediaPlayer并已启动播放,调用此方法才会生效。为了恢复一个暂停的MediaPlayer,可再次调用MediaPlayer.start()方法而无需任何准备。
通过调用下面的方法可停止播放:
MediaPlayer.stop();
注意,当我们想启动一个停止的MediaPlayer时,需要先再次调用MediaPlayer.prepare()方法。
我们可通过下面方法设置循环播放:
MediaPlayer.setLooping(true);
可通过下面的方法来调整音乐播放的音量:
MediaPlayer.setVolume(1, 1);
这会重新设置左右声道的音量,文档中没有指定这两个参数的设定范围。从多次尝试的结果看,有效值应该是0-1。
最后,我们需要一个方法来检查该播放是否完成,有两种方式可实现这一点。对于第一种方式,可向MediaPlayer注册一个OnCompletionListener,当播放完成时它就会被调用:
mediaPlayer.setOnCompletionListener(listener);
如果我们想轮询MediaPlayer的状态,则可使用下面方法:
boolean isPlaying = mediaPlayer.isPlaying();
注意,如果MediaPlayer被设置成循环播放,前面两个方法都无法指示该MediaPlayer已停止。
最后,如果MediaPlayer实例完成了所有的操作,就需要通过下面的方法来确保它所占用的资源得以释放:
mediaPlayer.release();
在丢弃一个实例前,执行这个操作应是很好的实践。
如果我们没有将MediaPlayer设置成循环且播放已结束,就可以通过MediaPlayer.prepare()和MediaPlayer.start()方法重启MediaPlayer。
大多数这些方法都是异步的,因此当调用MediaPlayer.stop()时,MediaPlayer.isPlaying()方法可能还需要一点时间才能返回。