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类中的相关方法进行音量控制。
posted @ 2017-12-05 18:22  灰色飘零  阅读(10497)  评论(0编辑  收藏  举报