【Android】安卓原生应用播放背景音乐与音效(笔记)


本文提供完整的音频管理器代码,涵盖了背景音乐(BGM)和短音效的播放控制。无论是游戏中的音效,还是应用中的背景音乐,通过 AudioManager,你可以方便地管理和控制音频资源。


前言

在 Android 开发中,音频处理是提升用户体验的重要部分,尤其在游戏、音效反馈以及媒体应用中,背景音乐(BGM)和短音效(如点击音效、通知音效)是常见的需求。通过有效的音频管理,能确保音效播放的流畅性和低延迟,以及背景音乐的播放控制。

本篇文章将展示如何通过封装一个 AudioManager 类,来统一管理背景音乐和短音效的播放。我们将实现以下功能:

  • 短音效:如点击、提示音等,通过 SoundPool 实现快速播放。
  • 背景音乐(BGM):适用于长时间播放的音频,通过 MediaPlayer 实现稳定播放。
  • 音频管理器:封装所有音频控制操作,方便全局调用,支持音效加载、播放、暂停、停止等功能。

音频播放机制

在 Android 中,音频播放通常分为两类:背景音乐和短音效。

MediaPlayer

使用MediaPlayer播放背景音乐,MediaPlayer 适合播放较长的音频文件,它支持流式播放,可以处理长时间的音频内容。

SoundPool

SoundPool 是专为短小音效设计的音频播放机制,适用于播放简单的音效(如短的 MP3、WAV 文件)。其特点是延迟低、资源占用少、支持同时播放多个音效,非常适合游戏或交互性较强的应用。

在 Android 中播放短音效(如短的 mp3、wav 文件),推荐用 SoundPool,专门为短小音效优化,延迟低、效率高!

如果用 MediaPlayer 播放短音效,性能没那么好,启动也慢一点。

初始化SoundPool

import android.media.AudioAttributes;
import android.media.SoundPool;private SoundPool soundPool;
private int soundId;

创建SoundPool实例

(适配 Android 5.0+ 和更低版本)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();soundPool = new SoundPool.Builder().setMaxStreams(5)  // 同时播放几个音效.setAudioAttributes(audioAttributes).build();
} else {// 老版本soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
}

加载音效文件

// 例如文件名是 sound_effect.mp3
soundId = soundPool.load(context, R.raw.sound_effect, 1);

播放音效

soundPool.play(soundId, 1f, 1f, 1, 0, 1f);
// 参数解释:
// soundId      -> 加载时返回的 id
// leftVolume   -> 左声道音量 (0-1)
// rightVolume  -> 右声道音量 (0-1)
// priority     -> 优先级(通常为 1)
// loop         -> 循环次数,0 表示不循环,-1 表示无限循环
// rate         -> 播放速率 (0.5-2.0)

释放资源

在 Activity 或 Fragment 销毁时调用

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

AudioManager

需求概述

我们的 AudioManager 类将负责以下操作:

  • 背景音乐(BGM)控制:播放、暂停、停止和循环。
  • 短音效控制:加载、播放、释放资源。
  • 支持 绝对路径 音频文件(如 /sdcard/…)。
  • 实现 单例模式,便于全局调用。

完整代码

import android.content.Context;
import android.media.AudioAttributes;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Build;
import android.util.Log;import java.io.IOException;
import java.util.HashMap;/*** AudioManager - 统一管理 BGM 和 音效*/
public class AudioManager {private static final String TAG = "AudioManager";private static AudioManager instance;// 音效private SoundPool soundPool;private HashMap<Integer, Integer> soundMap;private boolean soundPoolLoaded = false;// 背景音乐private MediaPlayer bgmPlayer;private boolean isBgmPrepared = false;private AudioManager() {soundMap = new HashMap<>();}public static AudioManager getInstance() {if (instance == null) {synchronized (AudioManager.class) {if (instance == null) {instance = new AudioManager();}}}return instance;}// -------------------- SoundPool - 音效部分 --------------------/*** 初始化 SoundPool*/public void initSoundPool(Context context) {if (soundPool != null) return;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).build();soundPool = new SoundPool.Builder().setMaxStreams(10).setAudioAttributes(audioAttributes).build();} else {soundPool = new SoundPool(10, android.media.AudioManager.STREAM_MUSIC, 0);}soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> {soundPoolLoaded = true;Log.d(TAG, "SoundPool loaded: " + sampleId);});}/*** 加载音效(绝对路径)*/public void loadSound(String absolutePath, int soundKey) {if (soundPool == null) {throw new IllegalStateException("SoundPool not initialized! Call initSoundPool(context) first.");}int soundId = soundPool.load(absolutePath, 1);soundMap.put(soundKey, soundId);}/*** 播放音效*/public void playSound(int soundKey) {if (!soundPoolLoaded) {Log.w(TAG, "SoundPool not loaded yet");return;}Integer soundId = soundMap.get(soundKey);if (soundId != null) {soundPool.play(soundId, 1f, 1f, 1, 0, 1f);} else {Log.w(TAG, "Sound key not found: " + soundKey);}}/*** 释放音效资源*/public void releaseSoundPool() {if (soundPool != null) {soundPool.release();soundPool = null;soundMap.clear();soundPoolLoaded = false;}}// -------------------- MediaPlayer - 背景音乐部分 --------------------/*** 播放背景音乐* @param absolutePath 文件绝对路径* @param looping 是否循环*/public void playBgm(String absolutePath, boolean looping) {stopBgm(); // 先停止再播放bgmPlayer = new MediaPlayer();try {bgmPlayer.setDataSource(absolutePath);bgmPlayer.setLooping(looping);bgmPlayer.setOnPreparedListener(mp -> {isBgmPrepared = true;mp.start();Log.d(TAG, "BGM started");});bgmPlayer.setOnCompletionListener(mp -> Log.d(TAG, "BGM completed"));bgmPlayer.prepareAsync();} catch (IOException e) {Log.e(TAG, "Error playing BGM: " + e.getMessage());}}/*** 暂停 BGM*/public void pauseBgm() {if (bgmPlayer != null && bgmPlayer.isPlaying()) {bgmPlayer.pause();Log.d(TAG, "BGM paused");}}/*** 继续播放 BGM*/public void resumeBgm() {if (bgmPlayer != null && !bgmPlayer.isPlaying() && isBgmPrepared) {bgmPlayer.start();Log.d(TAG, "BGM resumed");}}/*** 停止 BGM*/public void stopBgm() {if (bgmPlayer != null) {if (bgmPlayer.isPlaying()) {bgmPlayer.stop();}bgmPlayer.release();bgmPlayer = null;isBgmPrepared = false;Log.d(TAG, "BGM stopped and released");}}// -------------------- 全局释放 --------------------/*** 释放所有资源(退出时调用)*/public void releaseAll() {releaseSoundPool();stopBgm();}
}

播放音效

初始化音效池

AudioManager.getInstance().initSoundPool(context);

加载音效

这里采用绝对路径

AudioManager.getInstance().loadSound("/sdcard/sound/click.wav", 1);
AudioManager.getInstance().loadSound("/sdcard/sound/explosion.wav", 2);

播放音效

AudioManager.getInstance().playSound(1);

播放BGM

播放BGM

AudioManager.getInstance().playBgm("/sdcard/music/background.mp3", true);

暂停 / 继续播放

AudioManager.getInstance().pauseBgm();
AudioManager.getInstance().resumeBgm();

停止BGM

AudioManager.getInstance().stopBgm();

资源释放

AudioManager.getInstance().releaseAll();

注意事项

项目要点
BGM格式mp3、aac、wav,推荐 mp3
音效格式推荐 wav(低延迟),支持 mp3、ogg
路径绝对路径(如 /sdcard/yourdir/xxx.mp3)
权限动态申请 READ_EXTERNAL_STORAGE

动态权限申请:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/74132.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity | 游戏数据配置

目录 一、ScriptableObject 1.创建ScriptableObject 2.创建asset资源 3.asset资源的读取与保存 二、Excel转JSON 1.Excel格式 2.导表工具 (1)处理A格式Excel (2)处理B格式Excel 三、解析Json文件 1.读取test.json文件 四、相关插件 在游戏开发中,策划…

2025信创即时通讯排行:安全合规与生态适配双轮驱动

随着信息技术应用创新&#xff08;信创&#xff09;战略的深化&#xff0c;国产即时通讯工具在政企市场的渗透率显著提升。2025年作为“十四五”规划收官之年&#xff0c;信创产业迎来规模化应用关键节点。本文将从认证标准、市场表现、技术架构、行业适配四大维度&#xff0c;…

关于TVS管漏电流的问题?

问题描述&#xff1a; 在量产的带电池故事机生产中&#xff0c;工厂产线测试电流时&#xff0c;有1台机器电流比正常机器大10mA左右。 原因分析&#xff1a; 1、分析电路原理图&#xff0c;去除可能出现问题的电压或器件&#xff08;不影响系统&#xff09;&#xff0c;发现…

RAG 架构地基工程-Retrieval 模块的系统设计分享

目录 一、知识注入的关键前奏——RAG 系统中的检索综述 &#xff08;一&#xff09;模块定位&#xff1a;连接语言模型与知识世界的桥梁 &#xff08;二&#xff09;核心任务&#xff1a;四大关键问题的协调解法 &#xff08;三&#xff09;系统特征&#xff1a;性能、精度…

Java-servlet(七)详细讲解Servlet注解

Java-servlet&#xff08;七&#xff09;详细讲解Servlet注解 前言一、注解的基本概念二、Override 注解2.1 作用与优势2.2 示例代码 三、Target 注解3.1 定义与用途3.2 示例代码 四、WebServlet 注解4.1 作用4.2 示例代码 五、反射与注解5.1 反射的概念5.2 注解与反射的结合使…

机器学习——分类、回归、聚类、LASSO回归、Ridge回归(自用)

纠正自己的误区&#xff1a;机器学习是一个大范围&#xff0c;并不是一个小的方向&#xff0c;比如&#xff1a;线性回归预测、卷积神经网络和强化学都是机器学习算法在不同场景的应用。 机器学习最为关键的是要有数据&#xff0c;也就是数据集 名词解释&#xff1a;数据集中的…

本地AI大模型工具箱 Your local AI toolkit:LMStudio

LMStudio介绍 官网&#xff1a;LM Studio - Discover, download, and run local LLMs LMStudio 是一个面向机器学习和自然语言处理的&#xff0c;旨在使开发者更容易构建和部署AI语言模型的应用软件。 LMStudio的特点是&#xff1a; 完全本地离线运行AI大模型 可以从Huggi…

[OpenCV】相机标定之棋盘格角点检测与绘制

在OpenCV中&#xff0c;棋盘格角点检测与绘制是一个常见的任务&#xff0c;通常用于相机标定。 棋盘格自定义可参考: OpenCV: Create calibration pattern 目录 1. 棋盘格角点检测 findChessboardCorners()2. 棋盘格角点绘制 drawChessboardCorners()3. 代码示例C版本python版本…

redis的典型应用 --缓存

Redis最主要的用途&#xff0c;分为三个方面&#xff1a; 1.存储数据&#xff08;内存数据库&#xff09; 2.缓存&#xff08;最常用&#xff09; 3.消息队列 缓存 (cache) 是计算机中的⼀个经典的概念。核⼼思路就是把⼀些常⽤的数据放到触⼿可及(访问速度更快)的地⽅&…

本地基于Ollama部署的DeepSeek详细接口文档说明

前文&#xff0c;我们已经在本地基于Ollama部署好了DeepSeek大模型&#xff0c;并且已经告知过如何查看本地的API。为了避免网络安全问题&#xff0c;我们希望已经在本地调优的模型&#xff0c;能够嵌入到在本地的其他应用程序中&#xff0c;发挥本地DeepSeek的作用。因此需要知…

基于ArcGIS和ETOPO-2022 DEM数据分层绘制全球海陆分布

第〇部分 前言 一幅带有地理空间参考、且包含海陆分布的DEM图像在研究区的绘制中非常常见&#xff0c;本文将实现以下图像的绘制 关键步骤&#xff1a; &#xff08;1&#xff09;NOAA-NCEI官方下载最新的ETOPO-2022 DEM数据 &#xff08;2&#xff09;在ArcGIS&#xff08;…

自动化测试框架pytest+requests+allure

Pytest requests Allure 这个框架基于python的的 Pytest 进行测试执行&#xff0c;并结合 Allure插件 生成测试报告的测试框架。采用 关键字驱动 方式&#xff0c;使测试用例更加清晰、模块化&#xff0c;同时支持 YAML 文件来管理测试用例&#xff0c;方便维护和扩展。 测试…

Retrofit中scalars转换html为字符串

简介 在Retrofit中&#xff0c;如果你想直接获取HTML或其他文本格式的响应内容而不是将其映射到一个模型类&#xff0c;ScalarsConverterFactory 就派上用场了。ScalarsConverterFactory 是一个转换器工厂&#xff0c;它能够将响应体转换为Java基本类型如String、Integer或Byte…

Powershell WSL Windows系统复制数据到ubuntu子系统系统

从本地D盘下拷贝数据到ubuntu子系统下 Powershell 管理员打开执行 /mnt/d 此处是本地Windows系统的路径表示/opt ubutu 子系统目录 wsl -d Ubuntu-22.04 -u root -- bash -c cp -rf /mnt/d/nginx.conf /opt/从ubuntu子系统中拷贝数据到本地D盘下 Powershell 管理员打开执行…

【多线程】线程安全集合类,ConcurrentHashMap实现原理

文章目录 线程安全集合类解决方案多线程环境使用顺序表多线程环境使用队列多线程环境使用哈希表ConcurrentHashMap1. 缩小锁的粒度2. 充分使用 CAS3. 针对扩容操作 线程安全集合类 ArrayList、Queue、HsahMap… 都是线程不安全的 Vector、Stack、Hashtable 都是线程安全的&am…

spring-tx笔记

编程式事务与声明式事务的理解 补充&#xff1a;什么是事务&#xff1f; 事务是一个重要概念&#xff0c;尤其在数据库管理系统中。事务是指一组操作。&#xff0c;这些操作要么全部成功执行&#xff0c;要么全部不执行&#xff0c;确保数据的一致性和完整性 编程式事务 编…

Android第四次面试(Java基础篇)

一、Java 中的 DCL 单例模式 单例模式是设计模式中最常用的模式之一&#xff0c;其核心目标是确保一个类在程序中仅有一个实例&#xff0c;并提供全局访问点。在 Java 中&#xff0c;实现单例模式需要兼顾线程安全和性能优化。DCL&#xff08;Double-Checked Locking&#xff0…

Java-SpringBootWeb入门、Spring官方脚手架连接不上解决方法

一. Spring 官网&#xff1a;Spring | Home Spring发展到今天已经形成了一种开发生态圈&#xff0c;Spring提供了若干个子项目&#xff0c;每个项目用于完成特定的功能(Spring全家桶) Spring Boot可以帮助我们非常快速的构建应用程序、简化开发、提高效率 。 二. Spring Boot入…

1.7 无穷小的比较

1.定义 2.性质 3.无穷小的比较 3.1等价无穷小的性质 3.2 常见等价无穷小

StarRocks 升级注意事项

前段时间升级了生产环境的 StarRocks&#xff0c;从 3.3.3 升级到了 3.3.9&#xff0c;期间还是踩了不少坑所以在这里记录下。 因为我们的集群使用的是存算分离的版本&#xff0c;也是使用官方提供的 operator 部署在 kubernetes 里的&#xff0c;所以没法按照官方的流程进入虚…