共计 4218 个字符,预计需要花费 11 分钟才能阅读完成。
背景与痛点
移动端语音识别集成常面临三大挑战:
1. 网络波动敏感:流式识别对实时性要求高,弱网环境下容易中断
2. 音频处理复杂:需要处理采样率转换、噪音抑制等预处理操作
3. 状态管理困难:麦克风权限、录音状态、网络重试等逻辑交织

以电商直播场景为例,当主播用语音讲解商品时,识别延迟超过 500ms 就会导致字幕不同步。传统方案往往需要开发者自行实现音频分块和网络重传机制。
环境准备
账号申请
- 访问 火山引擎官网 注册账号
- 进入方舟管理台开通「语音识别服务」
- 在「应用管理」创建新应用,获取 AppKey 和 Token
Android 项目配置
在 app/build.gradle 中添加依赖:
dependencies {
// 核心 SDK
implementation 'com.volcengine:volc-speech-sdk:2.3.1'
// 协程支持
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
// 生命周期管理
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
}
在 AndroidManifest.xml 添加权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
核心实现
初始化语音引擎
class SpeechRecognitionHelper(
context: Context,
private val appKey: String,
private val token: String
) {
private val speechEngine by lazy {
SpeechRecognizer.create(
context,
Config.builder()
.appKey(appKey)
.token(token)
.language(LanguageType.ZH_CN) // 设置中文识别
.build()).apply {
// 设置音频格式:16kHz 采样率,单声道
audioFormat = AudioFormat(
sampleRate = 16000,
channelConfig = AudioFormat.CHANNEL_IN_MONO
)
}
}
}
流式识别生命周期管理
建议结合 Activity 的 Lifecycle 控制资源释放:
class MainActivity : AppCompatActivity() {
private lateinit var speechHelper: SpeechRecognitionHelper
override fun onCreate(savedInstanceState: Bundle?) {
// 初始化时绑定生命周期
lifecycle.addObserver(object : DefaultLifecycleObserver {override fun onDestroy(owner: LifecycleOwner) {speechHelper.release()
}
})
}
// 开始录音时
fun startRecording() {
lifecycleScope.launch {
try {
speechHelper.startStreaming(
onPartialResult = { text ->
// 实时更新 UI
runOnUiThread {tvResult.text = text}
},
onFinalResult = { text ->
// 最终识别结果
}
)
} catch (e: Exception) {showErrorToast("启动失败: ${e.message}")
}
}
}
}
音频分块上传策略
采用双缓冲机制平衡延迟与性能:
1. 主缓冲区:固定 100ms 的音频数据包
2. 备缓冲区:网络不佳时临时存储数据
class AudioBufferManager {private val mainBuffer = ByteArrayOutputStream()
private val backupBuffer = ByteArrayOutputStream()
fun addData(data: ByteArray) {
when {
// 网络正常时直接发送
isNetworkGood -> {speechEngine.sendAudioData(mainBuffer.toByteArray())
mainBuffer.reset()}
// 网络波动时存入备用缓冲区
else -> backupBuffer.write(data)
}
}
fun flushBackupData() {if (backupBuffer.size() > 0) {speechEngine.sendAudioData(backupBuffer.toByteArray())
backupBuffer.reset()}
}
}
性能优化
网络自适应策略
// 基于 ConnectivityManager 检测网络变化
val callback = object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: Network) {when (network.capabilities?.linkDownstreamBandwidthKbps) {in 0..100 -> setLowQualityMode()
in 101..500 -> setStandardMode()
else -> setHighQualityMode()}
}
}
// 设置不同网络质量下的参数
private fun setLowQualityMode() {
speechEngine.setOption(
OptionKey.VAD_ENABLE,
false // 弱网时关闭静音检测
)
}
本地音频缓存
使用环形缓冲区避免 OOM:
class AudioCircularBuffer(sizeInBytes: Int) {private val buffer = ByteArray(sizeInBytes)
private var head = 0
private var tail = 0
fun write(data: ByteArray) {if (data.size > buffer.size) throw IllegalArgumentException("数据过大")
System.arraycopy(
data, 0,
buffer, head,
min(data.size, buffer.size - head)
)
head = (head + data.size) % buffer.size
}
}
避坑指南
认证失败常见原因
- Token 过期:火山方舟 Token 默认 24 小时失效
- 包名不匹配:检查管理台登记的 Android 包名
- 权限缺失:缺少录音或网络权限时不会报错但会静默失败
流式状态同步
推荐使用 StateFlow 管理识别状态:
sealed class SpeechState {object Idle : SpeechState()
object Preparing : SpeechState()
data class Recording(val partialText: String) : SpeechState()
data class Error(val msg: String) : SpeechState()}
private val _speechState = MutableStateFlow<SpeechState>(SpeechState.Idle)
val speechState: StateFlow<SpeechState> = _speechState
// 在识别回调中更新状态
speechEngine.setListener(object : SpeechListener {override fun onStart() {_speechState.value = SpeechState.Preparing}
override fun onPartialResult(text: String) {_speechState.value = SpeechState.Recording(text)
}
})
进阶建议
结合 ViewModel 管理状态
class SpeechViewModel : ViewModel() {private val helper = SpeechRecognitionHelper(...)
private val _uiState = mutableStateOf(SpeechUiState())
val uiState: State<SpeechUiState> = _uiState
fun startRecording() {
viewModelScope.launch {
helper.startStreaming(
onPartialResult = { text ->
_uiState.value = _uiState.value.copy(
recognizedText = text,
isProcessing = true
)
}
)
}
}
}
Compose 集成示例
@Composable
fun SpeechScreen(viewModel: SpeechViewModel) {val state by viewModel.uiState.collectAsState()
Column {
when {
state.isProcessing -> {Text("识别中: ${state.recognizedText}")
CircularProgressIndicator()}
state.error != null -> {ErrorMessage(state.error)
}
else -> {StartButton { viewModel.startRecording() }
}
}
}
}
结语
通过火山方舟的流式语音识别 2.0,我们实测在 4G 网络下能达到 98% 的识别准确率,平均延迟控制在 300ms 以内。关键点在于:
1. 合理的音频分块策略(建议 100-200ms/ 包)
2. 完善的网络状态监控
3. 使用协程简化异步代码
下一步可以探索:
– 结合语音活动检测 (VAD) 减少无效传输
– 实现离线语音识别降级方案
– 添加自定义热词提升业务场景识别率
遇到具体问题时,建议多查阅 火山引擎官方文档 获取最新参数说明。
正文完
发表至: 移动开发
近三天内
