Android 音乐(音效)播放方式总结
一、音效的分类
音效按照作用的不同,可以将音效分为即时音效和背景音乐。两种音效在Android中的实现技术是不同的。
主要的实现方式为:SoundPool、MediaPlayer。
区别在于,MediaPlayer会在播放音频的时候,会占用大量的系统资源,并且播放的时候,还需要缓冲,有较大的时延。但是SoundPool的机制是将声音资源加载到内存中,然后在需要播放的地方进行播放,几乎没有时延,但是也正是因为这样的机制,限制了加载的文件的大小,不然会出现加载失败或者内存占用过大的情况,原则上SoundPool播放的音效的长度不应该超过7S。
所有如果需要实时播放短时间音效的话,可以使用SoundPool,如果需要播放背景音乐的话,建议使用MediaPlayer。
二、即时音效的播放—SoundPool
相关API文档地址:https://developer.android.com/reference/android/media/SoundPool.html
具体实现代码为:
public class SampleActivity extends Activity {
SoundPool sp; // 声明SoundPool的引用
HashMap<Integer, Integer> hm; // 声明一个HashMap来存放声音文件
int currStreamId;// 当前正播放的streamId
@Override
// 重写onCreate方法
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); // 设置layout
initSoundPool(); // 初始化声音池的方法
Button b1 = (Button) this.findViewById(R.id.Button01); // 获取播放按钮
b1.setOnClickListener // 为播放按钮添加监听器
(new OnClickListener() {
@Override
public void onClick(View v) {
playSound(1, 0); // 播放1号声音资源,且播放一次
// 提示播放即时音效
Toast.makeText(getBaseContext(), "播放即时音效", Toast.LENGTH_SHORT)
.show();
}
});
Button b2 = (Button) this.findViewById(R.id.Button02); // 获取停止按钮
b2.setOnClickListener // 为停止按钮添加监听器
(new OnClickListener() {
@Override
public void onClick(View v) {
sp.stop(currStreamId); // 停止正在播放的某个声音
// 提示停止播放
Toast.makeText(getBaseContext(), "停止播放即时音效", Toast.LENGTH_SHORT)
.show();
}
});
}
// 初始化声音池的方法
public void initSoundPool() {
sp = new SoundPool(4, AudioManager.STREAM_MUSIC, 0); // 创建SoundPool对象
hm = new HashMap<Integer, Integer>(); // 创建HashMap对象
hm.put(1, sp.load(this, R.raw.musictest, 1)); // 加载声音文件musictest并且设置为1号声音放入hm中
}
// 播放声音的方法
public void playSound(int sound, int loop) { // 获取AudioManager引用
AudioManager am = (AudioManager) this
.getSystemService(Context.AUDIO_SERVICE);
// 获取当前音量
float streamVolumeCurrent = am
.getStreamVolume(AudioManager.STREAM_MUSIC);
// 获取系统最大音量
float streamVolumeMax = am
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
// 计算得到播放音量
float volume = streamVolumeCurrent / streamVolumeMax;
// 调用SoundPool的play方法来播放声音文件
currStreamId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
}
}
三、背景音效的播放—MediaPlayer
因为SoundPool只适合播放不大于7秒的音效文件,限制较大。背景音乐一般用MediaPlayer来播放。想要很好的用MediaPlayer进行音/视频的播放,首先需要熟悉MediaPlayer的生命周期。这样不仅仅有利于开发人员开发出更加合理的代码,而且可以达到充分利用系统资源的目的。
MediaPlayer的生命周期
MediaPlayer的生命周期包括10种状态,每种状态下可以调用相应的方法来实现音/视频文件的管理或播放。其各个状态及状态间的关系可以用一个简单的流程图来表示,如图所示:
Idle 状态
使用new方法创建一个MediaPlayer对象或者调用了其reset方法时,改MediaPlayer对象处于Idle 状态。
但是通过两种方式进入的idle状态还是有些区别的,主要体现为:如果这个状态下调用了getDuration等方法,若通过reset进入idle状态的话会出发onErrorListener.onError,并且MediaPlayer会进入Error状态。如果是新创建的MediaPlayer对象,则不会触发onError,也不会进入Error状态
End 状态
通过release方法可以进入End状态,只要MediaPlayer对象不再被使用了,就应当尽快将其通过release方法释放掉,以释放其占用的软、硬件资源,这其中有些资源是互斥的(相当于临界资源)。如果MediaPlayer进入了End状态,则不会再进入其他的任何状态了。
Initialized 状态
这个状态比较简单,MediaPlayer调 用 setDataSource方法就进入了 Initialized状态,表示此时要播放的文件已经设置好了。
Prepared 状态
初始化完成之后还需要通过调用prepare或 prepareAsync方法进行准备,这两个方法一个是同 步的,一个是异步的。只有进入了 Prepared状态,才表明MediaPlayer到目前为止都工作正常,可以进行音乐文件的播放。
Preparing 状态
这个状态比较容易理解,主要是与prepareAsync异步准备方法配合,如果异步准备完成,会触发OnPreparedListener.onPrepared,进而进入Prepared状态。
Started 状态
MediaPlayer准备完成后,通过调用start方法,将进入Started状态。所谓Started状态 ,也就是播放中状态,开发中可以使用isPlaying方法测试MediaPlayer是否处于Started状态。 如果播放完毕,而又设置了循环播放,则 MediaPlayer仍然会处于Started状态。类似的,如果在该状态下MediaPlayer调用了 seekTo或 者 start方法均可以让MediaPlayer停 留 在 Started状态。
Pause 状态
Started状态下调用pause方法可以暂停播放,从而进入到Pause状态。MediaPlayer暂停后再次调用start方法则可以继续进行播放,并转到Started状态。暂停状态可以调用seekTo方法,这是不会改变状态的。
Stop状态
Started或Paused状态下均可以调用stop方法停止播放并进入Stop状态,而处于Stop状态的MediaPlayer想要重新播放,需要通过调用prepareAsync或prepare方法返回到先前的Prepared状态重新开始才可以。
Play backCompleted 状态
文件正常播放完毕,而又没有设置循环播放的话就进入该状态,并会触发OnCompletionListener 接口中的onCompletion方法。此时可以调用start方法重新从头播放文件,也可以调用stop方法停 止播放,或者调用seekTo方法来重新定位播放位置。
Error状态
由于某种原因 MediaPlayer出现了错误,则会触发OnErrorListener.onError回调方法,此时MediaPlayer即进入Error状态。及时捕捉并妥善处理这些错误是很重要的,这可以帮助应用程序及 时释放相关的软、硬件资源,也可以改善用户体验。如 果 MediaPlayer进入了 Error状态,可以通过调用reset方法来恢复,使得 MediaPlayer重新返 回 到 Idle状态。
总结
从上述对生命周期的总结介绍可以看出,某些情况发生的时候,MediaPlayer会回调特定监听接口中的事件处理方法。如果在开发中希望使用回调,则需要先向MediaPlayer注册实现了指定监听接口的监听器。
引申
从上述也可以看到,Ijkplayer和这个在方法名定义上,非常类似,目前看,生命周期这块的控制基本上也是和MediaPlayer一样的。或许这也就是为什么,现在播放器大家用Ijkplayer这么多的原因了。
四、音量控制—AudioManager
AudioManager类在Android系统中主要用来进行音/视频播放时的音量控制,使用时的基本步骤如下所列。
- 首先可以调用Activity对象的getSystemService(Context.AUDIO_SERVICE)方法获取AudioManager对象。
- 然后再调用AudioManager类中的相关方法进行音量控制。