4. 使用SoundPool播放短音效

任务说明

本案例实现一个Android音效播放器:将多个短音效文件预加载到SoundPool中,界面提供多个按钮,点击不同按钮播放对应音效,同时限制最大音流数以控制系统资源占用。

实现方案

使用SoundPool.Builder创建SoundPool实例,设置最大音流数为3(即最多同时播放3个音效,超出时系统按优先级策略处理)。通过AudioAttributes指定音效用途为USAGE_MEDIA。音效文件放在res/raw目录下,使用load()方法预加载,加载完成后通过setOnLoadCompleteListener确认就绪。播放时调用play()方法并传入音效ID。

代码实现

1. 音效资源准备

在res/raw/目录下放置三个短音效文件:effect_click.wav、effect_notify.wav、effect_success.wav(可自行替换为任意短wav或ogg文件)。

2. 主布局 activity_main.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="24dp">

    <Button
        android:id="@+id/btn_click"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="播放点击音效"
        android:textSize="18sp"/>

    <Button
        android:id="@+id/btn_notify"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="播放通知音效"
        android:textSize="18sp"/>

    <Button
        android:id="@+id/btn_success"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="播放成功音效"
        android:textSize="18sp"/>

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="音效未加载"
        android:textSize="14sp"/>
</LinearLayout>

3. MainActivity.java


package com.example.soundpooldemo;

import android.media.AudioAttributes;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private SoundPool soundPool;
    private int soundClickId;
    private int soundNotifyId;
    private int soundSuccessId;
    private boolean isLoaded = false;
    private TextView tvStatus;

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

        tvStatus = findViewById(R.id.tv_status);
        Button btnClick = findViewById(R.id.btn_click);
        Button btnNotify = findViewById(R.id.btn_notify);
        Button btnSuccess = findViewById(R.id.btn_success);

        // 创建SoundPool:最大音流数为3
        AudioAttributes audioAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .build();

        soundPool = new SoundPool.Builder()
                .setMaxStreams(3)
                .setAudioAttributes(audioAttributes)
                .build();

        // 预加载音效
        soundClickId = soundPool.load(this, R.raw.effect_click, 1);
        soundNotifyId = soundPool.load(this, R.raw.effect_notify, 1);
        soundSuccessId = soundPool.load(this, R.raw.effect_success, 1);

        // 监听加载完成
        soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
            @Override
            public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                if (status == 0) {
                    isLoaded = true;
                    tvStatus.setText("音效加载完成,可播放");
                } else {
                    tvStatus.setText("音效加载失败,错误码: " + status);
                }
            }
        });

        // 按钮点击播放
        btnClick.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playSound(soundClickId);
            }
        });

        btnNotify.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playSound(soundNotifyId);
            }
        });

        btnSuccess.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playSound(soundSuccessId);
            }
        });
    }

    private void playSound(int soundId) {
        if (!isLoaded) {
            tvStatus.setText("音效尚未加载完成,请稍候");
            return;
        }
        // 播放:左音量1.0,右音量1.0,优先级1,循环0次,播放速率1.0
        soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (soundPool != null) {
            soundPool.release();
            soundPool = null;
        }
    }
}

关键实现说明

  • 最大音流数控制:setMaxStreams(3)限制同时播放的音效数量。当第4个音效请求播放时,系统会根据优先级和播放时间自动停止一个已有音流。
  • 预加载机制:三个音效在onCreate中通过load()异步加载,加载完成后通过回调通知界面。
  • 播放参数:play()方法中左右声道音量均为1.0,优先级为1,循环次数为0(不循环),播放速率1.0(正常速度)。

验证方法

  1. 在Android Studio中创建新项目,将上述布局和代码复制到对应文件。
  2. 在res/raw/目录下添加三个短音效文件(wav或ogg格式,时长建议不超过3秒)。
  3. 连接真机或模拟器运行应用,观察界面显示"音效加载完成,可播放"。
  4. 快速连续点击三个按钮,确认能听到对应音效;快速点击4次以上,观察系统自动停止最早播放的音效(因最大音流数为3),验证最大音流数限制生效。
判定标准:应用启动后状态栏显示加载成功;点击每个按钮都能播放对应音效;连续快速点击多个按钮时,同时播放的音效不超过3个,且无应用崩溃或异常退出。
SoundPool 音效播放架构与音流控制 effect_click.wav R.raw.effect_click effect_notify.wav R.raw.effect_notify effect_success.wav R.raw.effect_success SoundPool maxStreams = 3 Stream 1 (播放中) Stream 2 (播放中) Stream 3 (播放中) 第4个请求 → 自动停止最早音流 按钮: 点击音效 按钮: 通知音效 按钮: 成功音效 用户 点击 触发 音效文件 (res/raw) SoundPool 管理器 活动音流 (最多3个) UI 按钮