Mobile Media API (MMAPI, JSR-135) 是 MIDP 1.0 可選包、MIDP 2.0 必選包。主流手機支持 WAV、MIDI、MP3 (部分)。本文演示最常見場景:**從 JAR 內部資源流式加載 WAV 文件並播放**,含完整異常處理與生命周期控制。
| 類/接口 | 包 | 職責 |
Manager | javax.microedition.media | 創建 Player、播放簡單音調、獲取支持格式列表 |
Player | javax.microedition.media | 媒體播放控制器 (start/stop/close, setLoopCount, setMediaTime) |
PlayerListener | javax.microedition.media | 異步事件回調 (END_OF_MEDIA, ERROR, DEVICE_UNAVAILABLE) |
VolumeControl | javax.microedition.media.control | 音量調節 (0-100)、靜音 |
二、標準播放流程 (三步走)
1. 獲取資源流
// WAV 文件位於 JAR 根目錄 /res/raw/autorun.wav
InputStream is = this.getClass().getResourceAsStream("/res/raw/autorun.wav");
// 注意:以 '/' 開頭表示從 classpath 根路徑 (JAR 根) 查找
2. 創建 Player
// MIME 類型:audio/x-wav (WAV)、audio/midi (MIDI)、audio/mpeg (MP3)
Player player = Manager.createPlayer(is, "audio/x-wav");
3. 實現 PlayerListener 異步處理 (生產級必備)
player.addPlayerListener(new PlayerListener() {
public void playerUpdate(Player player, String event, Object eventData) {
if (PlayerListener.END_OF_MEDIA.equals(event)) {
System.out.println("播放自然結束");
// 可在此觸發下一首、循環邏輯、釋放資源
} else if (PlayerListener.ERROR.equals(event)) {
System.err.println("播放錯誤: " + eventData);
} else if (PlayerListener.DEVICE_UNAVAILABLE.equals(event)) {
System.err.println("音頻設備被搶佔 (來電/錄音)");
}
}
});
4. 啟動播放
player.start(); // 異步非阻塞
// 設置循環次數 (0=無限循環, 1=播放一次, n=播放 n 次)
player.setLoopCount(3);
5. 生命周期管理 (MIDlet 整合)
public class SoundMIDlet extends MIDlet {
private Player player = null;
public SoundMIDlet() {
try {
InputStream is = getClass().getResourceAsStream("/Autorun.wav");
player = Manager.createPlayer(is, "audio/x-wav");
player.addPlayerListener(new PlayerListener() { /* 見上 */ });
} catch (IOException | MediaException e) {
System.err.println("Player 初始化失敗: " + e);
}
}
public void startApp() {
if (player != null) {
try { player.start(); }
catch (MediaException e) { System.err.println("Start 失敗: " + e); }
}
}
public void pauseApp() {
if (player != null) {
try { player.stop(); } catch (MediaException e) { /* ignore */ }
}
}
public void destroyApp(boolean unconditional) {
if (player != null) {
try { player.close(); } catch (MediaException e) { /* ignore */ }
player = null;
}
}
}
三、進階控制技巧
1. 精確定位與片段播放
// 獲取媒體總時長 (毫秒)
long duration = player.getDuration(); // TIME_UNKNOWN 表示不可知 (如流媒體)
// 設置播放位置
player.setMediaTime(5000); // 跳轉到 5 秒處
// 獲取當前播放位置
long current = player.getMediaTime();
2. 音量控制
VolumeControl vc = (VolumeControl) player.getControl("VolumeControl");
if (vc != null) {
vc.setLevel(50); // 0-100
vc.setMute(false);
}
3. 預取與緩衝 (減少首次啟動延遲)
player.prefetch(); // 預載媒體數據到緩衝區,阻塞直到 READY 狀態
// 隨後 start() 近乎瞬間啟動
// 適用於遊戲關鍵音效 (射擊、爆炸) 必須零延遲響應
四、格式兼容性與測試矩陣
| 格式 | MIME 類型 | 典型支持設備 | 備註 |
| WAV (PCM) | audio/x-wav | 幾乎所有 MIDP 2.0 手機 | 無壓縮、體積大、兼容性最好 |
| MIDI (SP-MIDI) | audio/midi | 絕大多數功能機/早期智能機 | 合成音樂、體積極小、音色依賴設備 |
| AMR-NB | audio/amr | 諾基亞 S40/S60、索愛 | 語音編碼、通話錄音常用 |
| MP3 | audio/mpeg | 高端智能機 (MIDP 2.0+ 專有擴展) | 授權費用高、解碼 CPU 佔用大 |
建議:遊戲音效統一用 **WAV (8kHz/16bit/單聲道)**,背景音樂用 **MIDI** 或 **AMR**,兼顧體積與兼容性。
五、常見坑點避坑指南
| 現象 | 原因 | 解決 |
| createPlayer 拋 MediaException | 格式不支持 / 數據損壞 / MIME 錯誤 | Manager.getSupportedContentTypes(null) 檢查支持列表;確認 MIME 大小寫 |
| start() 靜默失敗、無聲音 | 設備被佔用 / 音量為 0 / 流未預取 | prefetch() + VolumeControl 檢查;捕獲 MediaException |
| 播放中來電導致聲音中斷 | DEVICE_UNAVAILABLE 事件 | PlayerListener 中處理:pauseApp stop(), 通話結束 re-prefetch+start |
| JAR 體積過大導致安裝失敗 | WAV 文件過多/過大 | 降低採樣率 (8kHz)、單聲道、外部下載、動態生成音調 (Manager.playTone) |
| 模擬器正常、真機無聲 | 真機不支持該格式/採樣率 | 真機測試矩陣覆蓋主流機型;提供降級格式 |
六、結語
MMAPI 以 **Manager → Player → Control/Listener** 清晰分層,提供了功能完整的移動媒體播放能力。掌握 **資源流獲取、MIME 類型匹配、PlayerListener 異步處理、prefetch 零延遲啟動、生命周期綁定 MIDlet** 五大核心技能,即可在 J2ME 遊戲/應用中實現專業級音頻體驗。記住:**真機測試是唯一標準**,模擬器無法完全模擬硬件解碼器行為。
- 上一篇文章: J2ME RMS 進階應用:構建基於 String 鍵值的靈活存儲框架,突破 RecordID 單一索引限制
- 下一篇文章: 數據驅動命令界面生成架構:以數據庫四級表結構實現命令元素動態構建與熱更新
Copyright ©
程式碼之家 All Rights Reserved