下面是MediaPlayer和SoundPool类的对比特性:

1.soundpool可以播一些短的反应速度要求高的声音, 
比如游戏中的爆破声,而mediaplayer适合播放长点的。 
2. SoundPool载入音乐文件使用了独立的线程,不会阻塞UI主线程的操作。但是这里如果音效文件过大没有载入完成,我们调用play方法时可能产生严 重的后果,这里Android SDK提供了一个SoundPool.OnLoadCompleteListener类来帮助我们了解媒体文件是否载入完成,我们重载 onLoadComplete(SoundPool soundPool, int sampleId, int status) 方法即可获得。 
3. 从上面的onLoadComplete方法可以看出该类有很多参数,比如类似id,是的SoundPool在load时可以处理多个媒体一次初始化并放入内存中,这里效率比MediaPlayer高了很多。 
4. SoundPool类支持同时播放多个音效,这对于游戏来说是十分必要的,而MediaPlayer类是同步执行的只能一个文件一个文件的播放。

1. 游戏音效SoundPool

游 戏中会根据不同的动作 , 产生各种音效 , 这些音效的特点是短暂(叫声,爆炸声可能持续不到一秒) , 重复(一个文件不断重复播放) , 并且同时播放(比如打怪时怪的叫声 , 和技能释放的声音需要同时播放) , 即时(技能用处之后声音马上随着玩家操作发出,不能有延迟).

MediaPlayer会占用大量的系统资源 , 并且不能同时播放 , 并且无法实现即时音效 , 这里引入了一个新的类 -- SoundPool , 这个类完全满足上面提出的四点要求 , 可以无延时播放游戏中的短暂音效 .

2. 相关API介绍

(1) SoundPool

构造方法 : SoundPool(int maxStreams, int streamType, int srcQuality) ;

参数解析 :

maxStream : 该参数是定义最多能同时播放的多少音效 .

streamType : 该参数定义音频类型 , 游戏中一般设置为AudioManager.STREAM_MUSIC .

srcQuality : 该参数用来设置音频质量 , 这个参数目前没有作用 , 这里设置为 0;

加载音频文件方法 : int load(Context context, int resId, int priority);

参数解析 :

context : 上下文对象;

resId : 要加载的资源文件 , 即R.raw.music...

priority : 优先级别 , 这里没有作用 , 设置为1.

播放音效方法 : int play(int soundId, float leftVolume, float rightVolume, int priority, int loop, float rate);

参数解析 :

soundId : 这个id不是资源id , 指的是利用load方法加载资源文件返回的id值 , 这个要区别清楚.

leftVolume : 左声道的音量 , 这个音量是一个 0 ~ 1的数 , 这个小数是当前音量/最大音量的结果;

rightVolume : 右声道的音量 , 这个音量与左声道的音量是同一种音量;

priority : 优先级参数 , 0为最低, 这里设置为1;

loop : 音效循环的次数 , 0为不循环 , -1为永远循环;

rate : 音效回放的速度 , 这个值是在0.5~2.0f之间 , 1f是正常速度;

暂停音效播放方法 : pause(int streamId);

参数streamId : 这个参数是play()方法执行完之后的返回值 , 这个返回值是正在播放的音效的一个标识 , 对正在播放的音效进行操作的时候 , 就需要这个标识来对其进行操作;

通知音效播放方法 : stop(int streamId) , 这个参数与上面的pause()方法中的streamId参数是一个效果.

(2)AudioManager

获取方法 : AudioManager对象时系统服务, 可以通过调用上下文对象的getSystemService(Context.AUDIO_SERVICE)获取 , 注意获取到之后 , 需要将对象墙砖为AudioManager对象才可以使用.

eg : AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);

利用AudioManager获取当前音量的方法 : float currVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

利用AudioManager获取当前系统最大音量方法 : float maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);

使用这两个音量就可以计算出运行SoundPool音效的音量 , 当前音量 / 系统最大音量 , 结果就是soundPool.play()方法中需要传入的音量 ;

3. 程序代码

public class MainActivity extends Activity implements OnClickListener {  private SoundPool soundPool;  private HashMap<Integer, Integer> hashMap;  private int currStreamId;  @Override  public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  initSoundPool();  }  private void initSoundPool() {  soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);   hashMap = new HashMap<Integer, Integer>();  hashMap.put(1, soundPool.load(getApplicationContext(), R.raw.musictest, 1));  }  @Override  public void onClick(View v) {  switch (v.getId()) {  case R.id.bt_play:  play(1, 0);  Toast.makeText(getApplicationContext(), "播放即时音效", Toast.LENGTH_LONG).show();  break;  case R.id.bt_stop:  soundPool.stop(currStreamId);  Toast.makeText(getApplicationContext(), "暂停播放", Toast.LENGTH_LONG).show();  break;  default:  break;  }  }  private void play(int sound, int loop) {  AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);  float currVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);  float maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);  float volume = currVolume / maxVolume;  currStreamId = soundPool.play(hashMap.get(sound), volume, volume, 1, loop, 1.0f);  System.out.println(currStreamId);  }  } 

4. 程序的注意点

  • 音效文件需要放在res的raw下.
  • SoundPool播放的音效要小于7秒 , 否则会出现加载失败的现象;
  • 播放的大小尽量不超过1M,太大会影响播放;
  • 在Android平台上使用的即时文件越小越好 , 必要的时候可以降低采样频率或者将立体声改为单声道;
  • 都说MediaPlayer比较耗资源,在一样的情况下(文件一致),只使用一个MediaPlayer的对象的reset(),prepare(),start()这些方法速度的慢也体验不出来。SoundPool和MediaPlayer都可以使用,且相对而言MediaPlayer要稳定些;
  • 当调用load方法的时候实际就是把音效加载到了 SoundPool中,此时返回的streamId其实就是该音效在SoundPool中的Id,这个ID从0还是1来着(有点记不清了) 递增,不过要注意的是,不要超过  256  这个临界点。也就是说第257个声音加载进去后,调用play方法其实是播不出来的,说不定还会挤掉一些前面加载好的声音。这个256的限制通过查看SDK源码基本就能了解清楚,它底层就那么实现的,用一个类似堆栈来存;
  • 在创建的时候 maxStream这个参数代表能够同时播放的最大音效数,这里切忌合理使用,写的太大后会报AudioFlinger could not  create track, status: -12 。。。。一旦报了这个错,你就听不到声音了;
  • 如果你音效多,也不要指望unload方法来清除掉一些音效后再load新的进去,虽然unload后音效卸载了,但是前面分给它在SoundPool里面的Id可没有释放掉,也就是说这个时候你load新的进去只会在后面继续累加,然后累加多了就超过256了,然后就就听不到声音,然后就没有然后了。要想彻底清掉前面的音效请使用release方法,它会连内存中占用的资源一起释放掉;
  • load需要一点点时间,load后不要马上unload,load ---play--unload的做法并不可取,不要load太大的音效,它只会申请1M的内存空间。SoundPool出错后通常会看到return的值是0

SoundPool —— 适合短促且对反应速度比较高的情况(游戏音效或按键声等)

下面介绍SoundPool的创建过程:

1. 创建一个SoundPool (构造函数)

public SoundPool(int maxStream, int streamType, int srcQuality) 
maxStream —— 同时播放的流的最大数量
streamType —— 流的类型,一般为STREAM_MUSIC(具体在AudioManager类中列出)
srcQuality —— 采样率转化质量,当前无效果,使用0作为默认值

初始化一个实例:
SoundPool soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0); 
创建了一个最多支持5个流同时播放的,类型标记为音乐的SoundPool。

2. 加载音频资源 

可以通过四种途径来记载一个音频资源:
int load(AssetFileDescriptor afd, int priority) 
通过一个AssetFileDescriptor对象
int load(Context context, int resId, int priority) 
通过一个资源ID
int load(String path, int priority) 
通过指定的路径加载
int load(FileDescriptor fd, long offset, long length, int priority) 
通过FileDescriptor加载

*API中指出,其中的priority参数目前没有效果,建议设置为1。

一个SoundPool能同时管理多个音频,所以可以通过多次调用load函数来记载,如果记载成功将返回一个非0的soundID ,用于播放时指定特定的音频。

int soundID1 = soundPool.load(this, R.raw.sound1, 1);
if(soundID1 ==0){
    // 记载失败
}else{
   // 加载成功
}
int soundID2 = soundPool.load(this, R.raw.sound2, 1);
... 
这里加载了两个流,并分别记录了返回的soundID 。

需要注意的是, 
流的加载过程是一个将音频解压为原始16位PCM数据的过程,由一个后台线程来进行处理异步,所以初始化后不能立即播放,需要等待一点时间。

3. 播放控制 

有以下几个函数可用于控制播放:
final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) 
播放指定音频的音效,并返回一个streamID 。
        priority —— 流的优先级,值越大优先级高,影响当同时播放数量超出了最大支持数时SoundPool对该流的处理;
        loop —— 循环播放的次数,0为值播放一次,-1为无限循环,其他值为播放loop+1次(例如,3为一共播放4次).
        rate —— 播放的速率,范围0.5-2.0(0.5为一半速率,1.0为正常速率,2.0为两倍速率)
final void pause(int streamID) 
暂停指定播放流的音效(streamID 应通过play()返回)。
final void resume(int streamID) 
继续播放指定播放流的音效(streamID 应通过play()返回)。
final void stop(int streamID) 
终止指定播放流的音效(streamID 应通过play()返回)。

这里需要注意的是, 
1.play()函数传递的是一个load()返回的soundID——指向一个被记载的音频资源 ,如果播放成功则返回一个非0的streamID——指向一个成功播放的流 ;同一个soundID 可以通过多次调用play()而获得多个不同的streamID (只要不超出同时播放的最大数量);
2.pause()、resume()和stop()是针对播放流操作的,传递的是play()返回的streamID ;
3.play()中的priority参数,只在同时播放的流的数量超过了预先设定的最大数量是起作用,管理器将自动终止优先级低的播放流。如果存在多个同样优先级的流,再进一步根据其创建事件来处理,新创建的流的年龄是最小的,将被终止;
4.无论如何,程序退出时,手动终止播放并释放资源是必要的。

4. 更多属性设置 

其实就是paly()中的一些参数的独立设置:
final void setLoop(int streamID, int loop) 
设置指定播放流的循环.
final void setVolume(int streamID, float leftVolume, float rightVolume) 
设置指定播放流的音量.
final void setPriority(int streamID, int priority) 
设置指定播放流的优先级,上面已说明priority的作用.
final void setRate(int streamID, float rate) 
设置指定播放流的速率,0.5-2.0.

5. 释放资源 

可操作的函数有:
final boolean unload(int soundID) 
卸载一个指定的音频资源.
final void release() 
释放SoundPool中的所有音频资源.

下面对以上进行总结:

一个SoundPool可以:
1.管理多个音频资源,通过load()函数,成功则返回非0的soundID;
2.同时播放多个音频,通过play()函数,成功则返回非0的streamID;
3.pause()、resume()和stop()等操作是针对streamID(播放流)的;
4.当设置为无限循环时,需要手动调用stop()来终止播放;
5.播放流的优先级(play()中的priority参数),只在同时播放数超过设定的最大数时起作用;

6.程序中不用考虑(play触发的)播放流的生命周期,无效的soundID/streamID不会导致程序错误。

参考1:http://blog.csdn.net/qduningning/article/details/8680575

参考2:http://www.open-open.com/lib/view/open1390879650507.html

参考3:http://blog.csdn.net/xiaominghimi/article/details/6101737

android音效的加载方式相关推荐

  1. Android图片加载之认识bitmap的四种加载方式

    Android的Bitmap有四种高效加载方式 从获取方式分: (1)以文件流的方式 假设在sdcard下有 test.png图片 FileInputStream fis = new FileInpu ...

  2. android隐私违规获取问题处理 及 Hook拦截处理记录 (VirtualXposted/epic等)及 android/iOS 多bundle加载方式修复方案

    文章目录 1.如何检测/复现 android_id/Mac地址等权限被超前获取 ```但是,检测机构是如何检测我们的APP?``` 关于hooklogin插件 太极 ---- 以上基础工作完成---- ...

  3. Android性能优化之解密ZAKER,网易云阅读等新闻应用的内容缓存加载方式

    我是比较关注时事的, 每天都会花一点事件去看看新闻什么的. 因此类似ZAKER, 网易云阅读等这类的资讯聚合类应用是我的钟爱, 并且这些应用也确实做得很好,值得学习! 前面一篇文章, 讲了缓存的一些构 ...

  4. android fragment加载布局的方式,Android中Fragment的加载方式与数据通信详解

    Android中Fragment的加载方式与数据通信详解 发布时间:2020-08-22 18:55:57 来源:脚本之家 阅读:155 作者:Joah 一.加载方式 1. 静态加载 1.1 加载步骤 ...

  5. Android类动态加载技术

    Android类动态加载技术 Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的普通需求.但是有些特殊问题,常常引发我们进一步的沉思.我们从沉思中产生顿悟,从而产生新的技术形式 ...

  6. Android之图片加载框架Picasso源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76645535 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  7. android 加载进度,Android实现图片加载进度提示

    本文实例为大家分享了Android实现图片加载进度提示的具体代码,供大家参考,具体内容如下 先上图: 实现原理: 第一个控件的实现原理是重写ImageView的onDraw()方法,利用Canvas的 ...

  8. Android apk动态加载机制的研究(二):资源加载和activity生命周期管理

    转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/23387079 (来自singwhatiwanna的csdn博客) 前言 为了 ...

  9. Android Loader 异步加载详解二:探寻Loader内部机制

    Android Loader 异步加载详解二:探寻Loader内部机制 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/7025991 ...

最新文章

  1. 超强后浪:14岁考上研究生,如今17岁的他或将成为全国最小的博士生!
  2. mysql多条件查询_使用dsum轻松搞定多条件查询,学会它,再也想用sumifs了
  3. 程序人生之回顾大学前两年----第一篇
  4. Microsoft Dynamics CRM 2015 数据管理 之 如何批量导入数据到 正式区(二)系统自带示例数据 安装及教学...
  5. 一笔画问题 连通图(搜索+队列)
  6. tensorflow的tf.transpose()简单使用
  7. linux与windows双系统保持时间同步
  8. 共享内存简介及docker容器的shm设置与修改
  9. python os write_Python 3:写入方法与os.write返回的字节数
  10. php wp foo,WordPress 常用函数 / wpautop
  11. 西电计科院微机原理与系统设计课程笔记(车向泉版)
  12. java之StringBuilder和关于数组怎么扩容
  13. 电脑自动同步服务器时间bat,用Internet时间服务器来同步本机系统时间的批处理...
  14. BGP基本配置和路由聚合
  15. k8s学习-深入理解Pod对象
  16. 关于python语言、下列说法不正确的是-模拟试卷C【单项选择题】
  17. MySQL 数据库删除操作中的 delete、drop、 truncate 区别在哪?
  18. Python for循环遍历字典(dict)的方法
  19. mysql数据库引擎(show engines)
  20. css background-attachment属性详解

热门文章

  1. 戴尔游匣7559 Win10 固态硬盘系统重装
  2. htmlcss复习笔记-4
  3. 【NPDP】大师级管理人物盘点:竞争战略之父-迈克尔·波特
  4. CQUPT第九届ACM校赛 H 夺命毒奶
  5. 【无标题】DH460钢板交货状态,DH460钢板供应
  6. jQuery基本操作--制作QQ简易聊天框
  7. VScode 代码特效
  8. 6612345超级浏览器 集网页打印、读取身份证、拍照、读取串口等功能为一体的超级浏览器
  9. 广播模块加继电器怎么接线_继电器接线方法
  10. UnityThread子线程使用只能在主线程中调用的函数或Unity API