【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.pswp.cn/diannao/74132.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/74132.shtml
英文地址,請注明出處:http://en.pswp.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;所以沒法按照官方的流程進入虛…