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

Android应用ForegroundService失焦后Porcupine唤醒词检测失效求助

解决方案

1. 处理音频焦点冲突

Android系统中,当其他应用获取音频输入焦点时,Porcupine会被中断且默认不会自动恢复,这是导致应用失焦后检测停止的核心原因。需要主动管理音频焦点:

修改PorcupineService类,添加音频焦点监听逻辑:

class PorcupineService : Service(), AudioManager.OnAudioFocusChangeListener {
    private val accessKey = "nuh uh"
    private var porcupineManager: PorcupineManager? = null
    private val notificationId = "zingaraccia"
    private lateinit var audioManager: AudioManager

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate() {
        super.onCreate()
        audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
        // 申请音频焦点,配置为语音通信类型
        val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
            .setOnAudioFocusChangeListener(this)
            .setAudioAttributes(
                AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .build()
            )
            .build()
        audioManager.requestAudioFocus(focusRequest)

        val notification = Notification.Builder(this, notificationId)
            .setContentTitle("Zingaraccia Companion")
            .setContentText("Pronta a zingarare!")
            .setSmallIcon(R.mipmap.ic_launcher)
            .build()
        startForeground(1, notification)
    }

    override fun onAudioFocusChange(focusChange: Int) {
        when (focusChange) {
            AudioManager.AUDIOFOCUS_LOSS,
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                // 临时或永久失去焦点,暂停检测
                porcupineManager?.stop()
            }
            AudioManager.AUDIOFOCUS_GAIN -> {
                // 重新获取焦点,恢复唤醒词检测
                porcupineManager?.start()
            }
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        try {
            if (porcupineManager == null) {
                porcupineManager = PorcupineManager.Builder()
                    .setAccessKey(accessKey)
                    .setKeywordPath("models/zingaraccia.ppn")
                    .setModelPath("models/porcupine_params_it.pv")
                    .setSensitivity(0.7f)
                    .build(applicationContext) {
                        val inten = Intent(Intent.ACTION_VOICE_COMMAND)
                        inten.flags = Intent.FLAG_ACTIVITY_NEW_TASK
                        startActivity(inten)
                    }
            }
            porcupineManager?.start()
        } catch (e: PorcupineException) {
            Log.e("PORCUPINE_SERVICE", e.toString())
        }
        return START_STICKY // 服务被系统杀死后自动重启
    }

    override fun onBind(intent: Intent): IBinder? = null

    override fun onDestroy() {
        super.onDestroy()
        // 释放音频焦点
        audioManager.abandonAudioFocus(this)
        try {
            porcupineManager?.stop()
            porcupineManager?.delete()
        } catch (e: Exception) {
            Log.e("PORCUPINE", e.toString())
        }
    }
}

2. 确保动态权限正确授予

Android 6.0+需要动态申请录音权限,否则Porcupine无法访问麦克风。在MainActivity中添加权限申请逻辑:

class MainActivity : ComponentActivity() {
    private val recordAudioPermission = Manifest.permission.RECORD_AUDIO
    private val requestCode = 1001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (ContextCompat.checkSelfPermission(this, recordAudioPermission) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(recordAudioPermission), requestCode)
        } else {
            startService(Intent(this, PorcupineService::class.java))
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == this.requestCode && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            startService(Intent(this, PorcupineService::class.java))
        }
    }
}

3. 优化后台稳定性(可选)

对于Android 12+,可引导用户开启"不受限制的后台权限",避免系统在后台限制服务运行:

// 在MainActivity中添加引导逻辑
private fun requestUnrestrictedBackground() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
        intent.data = Uri.parse("package:$packageName")
        startActivity(intent)
    }
}

关键说明

  • 音频焦点管理是解决问题的核心:确保其他应用占用麦克风后,Porcupine能在焦点恢复时自动重启检测。
  • START_STICKY配置可提升服务稳定性,避免系统内存不足时被杀死后无法重启。
  • 动态权限必须提前申请,否则麦克风访问会被系统拦截,导致Porcupine无法工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:14:51