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
相关产品推荐
相关产品推荐

