You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用后台时热词检测服务被抢占麦克风后无法重启如何解决

解决方案

1. 补充前台服务必要的声明与权限

Android 10及以上系统要求麦克风相关前台服务必须显式声明服务类型,否则会被系统判定为非法后台访问,无法获取麦克风权限。

  • 在AndroidManifest.xml中为服务添加前台服务类型声明:
<service
    android:name=".HotKeyWordDetectionService"
    android:foregroundServiceType="microphone"
    android:exported="false" />
  • 补充对应权限声明:
<!-- 基础录音权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 前台服务基础权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Android 12+ 麦克风前台服务专属权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
  • 将onStartCommand的返回值从START_NOT_STICKY改为START_STICKY,保证服务被系统异常回收后可以自动重启,重新申请麦克风资源。

2. 添加音频焦点监听与资源释放逻辑

你当前的报错核心原因是麦克风被其他应用抢占时,没有彻底释放PocketSphinx占有的AudioRecord资源,重试时资源冲突导致创建录音实例失败。
需要在服务中注册音频焦点监听器,失去焦点时彻底释放识别器资源,重新拿到焦点后再初始化识别器:

private AudioManager audioManager;
private AudioFocusRequest audioFocusRequest;

@Override
public void onCreate() {
    super.onCreate();
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    // 配置音频焦点请求,适配Android 8.0+
    audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
            .setAudioAttributes(new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_ASSISTANT)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .build())
            .setOnAudioFocusChangeListener(focusChange -> {
                if (focusChange == AudioManager.AUDIOFOCUS_LOSS || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
                    // 失去音频焦点,彻底释放识别器资源
                    releaseRecognizer();
                } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN && recognizer == null) {
                    // 重新获得焦点,初始化识别器
                    init();
                }
            }).build();
    // 请求音频焦点
    audioManager.requestAudioFocus(audioFocusRequest);
}

private void releaseRecognizer() {
    if (recognizer != null) {
        recognizer.cancel();
        recognizer.stop();
        recognizer.shutdown();
        recognizer = null;
    }
}

同时修改init方法,每次初始化前先释放旧资源,初始化失败时添加延迟重试逻辑:

public void init() {
    service.submit(() -> {
        try {
            // 先释放旧资源避免冲突
            releaseRecognizer();
            Assets assets = new Assets(HotKeyWordDetectionService.this);
            File assetDir = assets.syncAssets();
            setupRecognizer(assetDir);
            Log.i("sphinx_test", "setup success: ");
            handler.sendEmptyMessage(0);
        } catch (IOException e) {
            Log.i("sphinx_test", "setup failed: "+"Failed to init recognizer " + e.getMessage());
            e.printStackTrace();
            // 初始化失败,延迟3秒重试,避免频繁申请导致系统拦截
            handler.postDelayed(() -> init(), 3000);
        }
    });
}

3. 补充麦克风状态轮询兜底逻辑

如果部分第三方应用抢占麦克风时没有触发系统音频焦点回调,可以添加定时轮询逻辑,每隔3秒检查当前麦克风占用状态:

private final Runnable micCheckRunnable = new Runnable() {
    @Override
    public void run() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            List<AudioRecordingConfiguration> configs = audioManager.getActiveRecordingConfigurations();
            // 没有其他应用录音,且当前识别器未初始化
            if (configs.isEmpty() && recognizer == null) {
                init();
            }
        }
        handler.postDelayed(this, 3000);
    }
};

// 在服务onStartCommand中启动轮询
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    startForeground(1, createNotification());
    service = Executors.newFixedThreadPool(3);
    handler.post(micCheckRunnable);
    init();
    return START_STICKY;
}

// 服务销毁时停止轮询
@Override
public void onDestroy() {
    super.onDestroy();
    handler.removeCallbacks(micCheckRunnable);
    releaseRecognizer();
    audioManager.abandonAudioFocusRequest(audioFocusRequest);
}

内容的提问来源于stack exchange,提问作者SSP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 18:39:04