Browse Source

修复安卓切换到后台不能继续录音的bug

weicu
fdp 1 year ago
parent
commit
cfa77f275e
  1. 15
      local_plugins/azure_speech/android/src/main/AndroidManifest.xml
  2. 125
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/AudioRecordingForegroundService.kt
  3. 16
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt

15
local_plugins/azure_speech/android/src/main/AndroidManifest.xml

@ -3,4 +3,17 @@
package="com.yunqiinnovation.azure_speech">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>
<!-- 添加前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<!-- 注册音频录制前台服务 -->
<service
android:name=".tools.AudioRecordingForegroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="microphone" />
</application>
</manifest>

125
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/AudioRecordingForegroundService.kt

@ -0,0 +1,125 @@
package com.yunqiinnovation.azure_speech.tools
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import android.util.Log
/**
* 音频录制前台服务
* 用于在录制音频时保持应用在前台运行,防止系统杀死应用
*/
class AudioRecordingForegroundService : Service() {
companion object {
private const val TAG = "AudioRecordingService"
private const val NOTIFICATION_ID = 1001
private const val CHANNEL_ID = "audio_recording_channel"
private const val CHANNEL_NAME = "音频录制服务"
/**
* 启动前台服务
* @param context 上下文
*/
fun startService(context: Context) {
val intent = Intent(context, AudioRecordingForegroundService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
Log.d(TAG, "音频录制前台服务已启动")
}
/**
* 停止前台服务
* @param context 上下文
*/
fun stopService(context: Context) {
val intent = Intent(context, AudioRecordingForegroundService::class.java)
context.stopService(intent)
Log.d(TAG, "音频录制前台服务已停止")
}
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "AudioRecordingForegroundService onCreate")
createNotificationChannel()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "AudioRecordingForegroundService onStartCommand")
// 创建前台通知
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
// 返回START_STICKY确保服务被系统杀死后会重启
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "AudioRecordingForegroundService onDestroy")
stopForeground(true)
}
/**
* 创建通知渠道(Android 8.0及以上需要)
*/
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW
).apply {
description = "用于音频录制时保持应用运行"
setShowBadge(false)
setSound(null, null)
enableVibration(false)
}
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
/**
* 创建前台服务通知
* @return 通知对象
*/
private fun createNotification(): Notification {
// 创建点击通知时的意图(可以打开应用主界面)
val intent = packageManager.getLaunchIntentForPackage(packageName)
val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("语音助手正在运行")
.setContentText("正在录制音频,点击返回应用")
.setSmallIcon(android.R.drawable.ic_btn_speak_now) // 使用系统默认麦克风图标
.setContentIntent(pendingIntent)
.setOngoing(true) // 设置为持续通知,用户无法滑动删除
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setShowWhen(false)
.build()
}
}

16
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt

@ -17,6 +17,7 @@ import android.media.AudioFocusRequest
import android.media.AudioAttributes
import com.yunqiinnovation.azure_speech.tools.RecordFile
import android.os.Build // 新增:用于获取设备信息
import com.yunqiinnovation.azure_speech.tools.AudioRecordingForegroundService
/**
* 简单音频接收器类,用于处理音频录制和流传输
@ -208,6 +209,10 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba
Log.d("tag", "存下来的数据: ${writeQueue?.size}")
}
// 启动前台服务
AudioRecordingForegroundService.startService(context)
Log.d(TAG, "前台服务已启动,开始音频录制")
when (audioSourceType) {
AudioSourceType.MICROPHONE -> runMicrophoneCapture()
AudioSourceType.EXTERNAL -> runExternalCapture()
@ -218,10 +223,11 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba
pushAudioStream?.close()
pushAudioStream = null
isWriting.set(false)
// 停止前台服务
AudioRecordingForegroundService.stopService(context)
throw e // 重新抛出异常让调用者处理
}
}
// ... existing code ...
/**
* 配置通话音频模式以优化回声消除
*/
@ -392,6 +398,9 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba
try {
writeQueue.clear()
isWriting.set(false)
// 停止前台服务
AudioRecordingForegroundService.stopService(context)
// 停止录音
audioRecord?.stop()
@ -403,7 +412,6 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba
Log.e(TAG, "停止麦克风捕获失败: ${e.message}")
}
}
/**
* 释放音频资源
*/
@ -411,7 +419,9 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba
try {
isRunning.set(false)
isWriting.set(false)
// 停止前台服务
AudioRecordingForegroundService.stopService(context)
Log.d(TAG, "前台服务已停止,音频资源已释放")
// 中断并等待捕获线程结束
writeThread?.interrupt()
writeThread?.join(300) // 最多等待300ms

Loading…
Cancel
Save