一、音效的分类

音效按照作用的不同,可以将音效分为即时音效和背景音乐。两种音效在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类中的相关方法进行音量控制。

Android 音乐(音效)播放方式总结的更多相关文章

  1. Android点赞音效播放

    /** * 音效播放 */ private SoundPool mPool; /** * 音效id */ private int voiceID; voiceID = initSoundPool(); ...

  2. cocos2d安卓android长音效播放不完全

    是因为安卓限制了音效的内存,一般把mp3的比特率压缩一下.就可以搞定了.

  3. iOS开发系列--音频播放(音效和音乐)播放本地的

    音频 在iOS中音频播放从形式上可以分为音效播放和音乐播放.前者主要指的是一些短音频播放,通常作为 点缀音频,对于这类音频不需要进行进度.循环等控制.后者指的是一些较长的音频,通常是主音频,对于这些音 ...

  4. Android音乐播放器的开发实例

    本文将引导大家做一个音乐播放器,在做这个Android开发实例的过程中,能够帮助大家进一步熟悉和掌握学过的ListView和其他一些组件.为了有更好的学习效果,其中很多功能我们手动实现,例如音乐播放的 ...

  5. iOS: 音效和音乐的播放,封装的工具类

    在iOS中音频播放从形式上可以分为音效播放和音乐播放.前者主要指的是一些短音频播放,通常作为点缀音频,对于这类音频不需要进行进度.循环等控制.后者指的是一些较长的音频,通常是主音频,对于这些音频的播放 ...

  6. Android音乐播放器源码(歌词.均衡器.收藏.qq5.0菜单.通知)

    一款Android音乐播放器源码,基本功能都实现了 qq5.0菜单(歌词.均衡器.收藏.qq5.0菜单.通知) 只有向右滑动出现,菜单键和指定按钮都还没有添加. 源码下载:http://code.66 ...

  7. iOS开发--音乐文件播放工具类的封装(包含了音效的封装)

    一.头文件 #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface ...

  8. 一款非常简单的android音乐播放器源码分享给大家

    一款非常简单的android音乐播放器源码分享给大家,该应用虽然很小,大家常用的播放器功能基本实现了,可能有点还不够完善,大家也可以自己完善一下,源码在源码天堂那里已经有了,大家可以到那里下载学习吧. ...

  9. Android命令行播放MP3音乐

    /*************************************************************************** * Android命令行播放MP3音乐 * 说 ...

随机推荐

  1. python Django 无法获取post 参数问题

    对于 request.POST.get(name) 方式取值,需要 from 表单提交数据,如果 是ajax 提交数据,则需要做如下设置: 1.设置请求头,以from表单方式传值 'Content-T ...

  2. django-内网项目上线测试部署步骤

    1.安装python环境 由于测试环境只有内网,所以在外网同系统上安装python. wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5 ...

  3. Redux-persist使用

    redux-persist作用是将store中的数据缓存到浏览器中,减少数据请求,每当白名单中的数据发生变化,才会进行一次更新缓存的操作,并且这个数据缓存是存在localStorage中的,不是会话级 ...

  4. JS event loop

    一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...

  5. flume知识点总结

    首先介绍一下在flume中常用的一个数据格式,以及使用该格式的优缺点:  从flume写数据到hdfs中的时候,使用二进制格式相对于使用纯文本来说是一种更好的选择,因为大多数二进制格式都有一些方法指明 ...

  6. Java获取工程目录

    背景:程序执行时,会涉及到去读取配置文件等操作,那就需要了解怎么获得文件路径   Java目录映射关系 说明一点:在Java代码执行时,会将编译生成的classes文件,以及配置文件等信息生成到tar ...

  7. 清理xcode

    移除 Xcode 运行安装 APP 产生的缓存文件(DerivedData) ~/Library/Developer/Xcode/DerivedData 移除 APP 打包的ipa历史版本(Archi ...

  8. FortiGate部分用户上网慢,丢包严重

    1.现状: 如图,出口internet有2条联通线路分别为liant_218和liant_61,在防火墙上使用WAN LLB,基于源IP: 2.现象: 使用liant_218的用户上网正常,使用lia ...

  9. [leetcode]99. Recover Binary Search Tree恢复二叉搜索树

    Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...

  10. vue 高阶 provide/inject

    1.一般情况使用都是在app.vue配置为: provide () {return {isTest: this}}, 2.所有子组件都可以引用 拿到app.vue里面的所有数据 inject: ['i ...