处理相同的回调(AUDIOFOCUS_LOSS_TRANSIENT)

125 阅读2分钟

🚨 核心矛盾:相同的回调(AUDIOFOCUS_LOSS_TRANSIENT)却需不同响应

deepseek_mermaid_20250701_821352.png

💡 专业解决方案:AppB必须通过双重判断机制识别真实场景!


🧠 专业级处理方案(四步破解)

1️⃣ 第一步:统一暂停(基础响应)

java

// 收到任何 AUDIOFOCUS_LOSS_TRANSIENT 先暂停
public void onAudioFocusChange(int focusChange) {
    if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
        mediaPlayer.pause(); // 所有场景先暂停
        startEmergencyCheck(); // 启动紧急静音检测
    }
}

2️⃣ 第二步:检测系统级静音(独家秘笈)

java

// 监听系统强制静音信号(Android 8.0+)
private void startEmergencyCheck() {
    new Handler().postDelayed(() -> {
        // 关键检测:系统是否强制静音了音频流
        if (audioManager.isStreamMute(AudioManager.STREAM_MUSIC)) {
            activateEmergencyMute(); // 紧急静音模式
        } else {
            normalPauseMode(); // 普通暂停模式
        }
    }, 150); // 延迟150ms等待系统响应
}

⚠️ 原理:系统处理 TRANSIENT_EXCLUSIVE 时会强制静音音频流,而普通 TRANSIENT 不会

3️⃣ 第三步:分场景精细化处理

java

// 场景1:普通短暂打断(如导航提示)
private void normalPauseMode() {
    keepBuffering(); // 保持缓冲
    showPauseIcon(); // 显示暂停图标
    // 等待焦点返回自动恢复
}

// 场景2:VIP独占打断(如电话接通)
private void activateEmergencyMute() {
    mediaPlayer.setVolume(0, 0); // 双重静音
    releaseDecoderResources(); // 释放解码器资源
    showInterruptedBadge(); // 显示"被电话打断"提示
}

4️⃣ 第四步:焦点恢复时的差异处理

java

case AudioManager.AUDIOFOCUS_GAIN:
    if (isEmergencyMuted) {
        reinitializePlayer(); // 独占打断需重新初始化播放器
    } else {
        mediaPlayer.start(); // 普通暂停直接恢复
    }
    break;

🔍 深度技术解析(为什么必须这样做?)

判断维度TRANSIENTTRANSIENT_EXCLUSIVE
系统行为仅发送回调发送回调+强制静音音频流
音频流状态仍可播放(但应暂停)被系统设为静音
恢复成本低(直接播放)高(可能需重新初始化)
用户感知音乐暂停但设备未静音整个设备突然静音

🛠️ 终极代码方案(兼容Android 5.0+)

java

public class SmartAudioPlayer implements AudioManager.OnAudioFocusChangeListener {
    private boolean isExclusiveInterrupt;
    
    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                handleTransientLoss();
                break;
            // 其他case...
        }
    }

    private void handleTransientLoss() {
        // 先统一暂停
        mediaPlayer.pause();
        
        // 延迟检测静音状态
        new Handler().postDelayed(() -> {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 现代系统检测API
                isExclusiveInterrupt = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
            } else {
                // 传统系统通过音量检测
                int vol = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                isExclusiveInterrupt = (vol == 0);
            }
            
            if (isExclusiveInterrupt) {
                onExclusiveInterrupt();
            }
        }, 150); // 经验值延迟
    }

    private void onExclusiveInterrupt() {
        // 1. 保存播放状态
        savePlaybackState();
        
        // 2. 释放关键资源
        mediaPlayer.release();
        
        // 3. 监听系统静音解除
        registerMuteMonitor(); 
    }
    
    // 监听系统静音状态变化
    private void registerMuteMonitor() {
        audioManager.registerAudioDeviceCallback(new AudioDeviceCallback() {
            @Override
            public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
                checkAudioUnmute();
            }
        }, null);
    }
    
    private void checkAudioUnmute() {
        if (!audioManager.isStreamMute(AudioManager.STREAM_MUSIC)) {
            restorePlayback(); // 系统解除静音后恢复
        }
    }
}

💎 顶级专家建议

  1. 延迟检测值:150ms是经验值(覆盖系统处理延迟),可实测调整

  2. 双保险策略

    java

    // 在onExclusiveInterrupt()中增加:
    mediaPlayer.setVolume(0, 0); // 应用层静音
    audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, 
         AudioManager.ADJUST_MUTE, 0); // 同步系统层
    
  3. Android 13+ 优化:使用 AudioFocusRequest.Builder.setAcceptsDelayedFocusGain() 处理复杂场景

  4. 设备兼容性:华为/小米等定制ROM需单独测试静音API

🌟 精髓总结:相同的回调只是战争的开始,需要通过监听系统状态+延迟探测识别战场真相。就像急诊医生看到病人昏迷,会立即查瞳孔反射判断是普通晕厥还是脑死亡!