Android的SoundPool

2020-09-12

关键字:


 

1、SoundPool是什么?

 

正如其名所示,SoundPool就是一个声音池,里面存放了一系列的声音资源可供随时使用。

 

SoundPool是Android提供的专用于管理存储在应用程序raw资源以及SD卡上的声音资源的工具类。其主要应用场景是在对某些音频有需要经常重复播放的时候。例如,在一些游戏场景里,某些音效是会有高频的重复播放的需求的。

 

那为什么有高频重复播放的需求时用SoundPool来管理会更合适一点呢?

 

其实,我们当然也可以直接使用MediaPlayer来实现音频的重复播放。

 

但是,MediaPlayer在应对这种需要重复播放的场景时会有一个问题:MediaPlayer每次在播放前至少都需要先加载资源,然后开始播放,播放完成后又会释放掉资源。

 

MediaPlayer的这种机制本身是一种优点,它能最大限度地降低内存消耗量。但因为我们现在遇到的是需要重复播放相同的音频资源,甚至会是高频重复播放,这样一来,这种加载与释放的操作就会大大增加CPU的无谓计算并直接导致播放操作延时响应。

 

为了解决这种“缺陷”,就有了SoundPool。

 

SoundPool的原理也简单,它的本质还是使用MediaPlayer来解码并播放的,只是它在将音频从文件系统加载进内存后就一直在内存上托管着不释放。并为每个音频生成一个索引号,以后每次需要播放时就直接顺着这个索引号找到已经位于内存中的音频进行解码并播放即可。如此一来,就能相较于传统的MediaPlayer模式大大提升响应性并减少CPU计算开销。

 

不过,凡事皆有两面性。SoundPool是用空间来换取MediaPlayer在时间上的“缺陷”的,在内存中托管所有音频资源的SoundPool肯定是很吃内存的。这一特性同时也意味着SoundPool只适用于管理那些比较“短小精悍”的音频资源。与此同时,SoundPool的使用也同样短小精悍。

 

 

2、SoundPool怎么用?

 

这里只贴出SoundPool的关键用法,更多具体的用法与释义还请直接参考官方API: https://developer.android.google.cn/reference/android/media/SoundPool?hl=en

 

首先是SoundPool的实例化。

 

SoundPool推荐通过 SoundPool.Builder 来创建对象,不说那么复杂,对于一般用途来说,直接套用下面的构造代码就行了:

AudioAttributes.Builder builder = new AudioAttributes.Builder();
builder.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION);
builder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);

SoundPool.Builder builder2 = new SoundPool.Builder()
        .setMaxStreams(2)
        .setAudioAttributes(builder.build());

SoundPool soundPool = builder2.build();

 

 

其次是加载声音资源的方法:

public int load(Context context, int resId, int priority);
public int load(String path, int priority);
public int load(AssetFileDescriptor afd, int priority);
public int load(FileDescriptor fd, long offset, long length, int priority);

load()方法的作用就是将声音从文件系统中加载进内存以供播放。以上几种方法重载是分别适用于加载不同地方的声音文件的。不过因为SoundPool一般都只用于加载音效声音,因此,用的最多的还是第一种方法,直接加载APP资源目录raw下的声音文件。

 

声音文件的加载是异步的,只有在加载成功了才能播放。因此 load() 通常还会与加载结果回调监听设置方法配合使用:

public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener);

 

 

最后是其它的常用方法:

public final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate);
public final boolean unload(int soundID);
public final void release();

play() 方法的第2、第3个参数表示的是左右声道的音量。其值有效范围是 0 ~ 1,浮点型。

loop 参数用于控制声音的循环播放次数。-1表示无限循环播放。0表示不循环,只播放一次。正整数表示需要循环的次数。对于被设定为无限循环播放的声音,可以通过 stop() 或 unload() 来结束播放。

rate 参数控制的是声音播放的速率,即倍速播放,值范围为 0.5 ~ 2.0。

返回值代表的是当前正在播放的声音的索引号,通过这个索引号可以在播放过程中做一些控制操作,如 pause(), resume(), setPriority(), setVolume() 等。

 

unload() 方法则是将指定的声音资源从内存中卸载掉。

 

release() 方法则是将SoundPool中所有的声音资源都卸载掉。这个方法一般用于销毁对象时使用,一般在调用了这个方法以后也最好将SoundPool的变量手动置一下null以方便GC回收。

 

 

3、SoundPool的代码示例 

 

package com.chorm.demo;

import androidx.appcompat.app.AppCompatActivity;

import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private int audio1;
    private int audio2;
    private int audio3;

    private SoundPool soundPool;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //配置声音类型。
        AudioAttributes.Builder builder = new AudioAttributes.Builder();
        builder.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION);
        builder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);

        //配置SoundPool的属性。
        SoundPool.Builder builder2 = new SoundPool.Builder()
                .setMaxStreams(2) //可以同时播放的声音数量
                .setAudioAttributes(builder.build());

        soundPool = builder2.build();

        //配置SoundPool。
        audio1 = soundPool.load(this, R.raw.alya, 10);
        audio2 = soundPool.load(this, R.raw.beep, 8);
        audio3 = soundPool.load(this, R.raw.deneb, 3);
        soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
            @Override
            public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                // Do something。
            }
        });
    }

    public void sound1(View view){
        soundPool.play(audio1, 1, 1.0f, 1, 2, 1);
    }

    public void sound2(View view){
        soundPool.play(audio2, 1, 1, 8, 0, 1);
    }

    public void sound3(View view){
        soundPool.play(audio3, 1, 1, 3, 0, 1.0f);
    }
}

上述源码中的三个声音文件是位于笔者示例APP中的 raw 目录下的 ogg 音频文件。

 


 

posted @ 2020-09-12 19:10  大窟窿  阅读(1075)  评论(0编辑  收藏  举报