共计 2806 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
移动端语音识别看似简单,实际开发中常遇到以下问题:

- 环境噪音干扰 :手机麦克风采集的音频常混入背景杂音,导致识别准确率骤降
- 多语言支持 :中文混合英文的语音输入处理不当会出现乱码
- 连续识别稳定性 :长时间录音时容易因内存不足或 Session 超时中断
技术方案选型
Google Speech-to-Text API
适合需要云端处理的场景:
- 优势:支持 100+ 语言、自动标点、支持离线包下载
- 缺点:依赖网络、有 QPS 限制(免费版每分钟 60 次)
ML Kit 本地方案
适合实时性要求高的场景:
- 优势:离线运行、延迟 <300ms、无网络费用
- 缺点:仅支持 8 种语言、模型文件占用 15MB 存储空间
核心实现步骤
1. 基础环境配置
在 build.gradle 中添加依赖:
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'com.google.android.gms:play-services-mlkit-speech-recognition:16.1.0'
}
2. 动态权限申请
注意 Android 11+ 需要额外申请 MANAGE_EXTERNAL_STORAGE 权限:
private fun checkPermissions() {
val requiredPermissions = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.INTERNET
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
requestPermissions(requiredPermissions +
arrayOf(Manifest.permission.MANAGE_EXTERNAL_STORAGE),
REQUEST_CODE)
} else {requestPermissions(requiredPermissions, REQUEST_CODE)
}
}
3. SpeechRecognizer 初始化
关键参数说明:
- LANGUAGE_MODEL_FREE_FORM:适合自然对话场景
- SAMPLE_RATE_16000:平衡识别精度与带宽消耗的最佳采样率
val speechRecognizer = SpeechRecognizer
.createSpeechRecognizer(context).apply {
setRecognitionListener(object : RecognitionListener {override fun onResults(results: Bundle) {
// 处理识别结果
val matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
matches?.firstOrNull()?.let {showText(it)
}
}
override fun onError(error: Int) {when (error) {
SpeechRecognizer.ERROR_NO_MATCH ->
toast("未识别到语音")
ERROR_SPEECH_TIMEOUT ->
restartListening()}
}
})
}
fun startListening() {val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
putExtra(RecognizerIntent.EXTRA_SAMPLE_RATE, 16000)
}
speechRecognizer.startListening(intent)
}
性能优化实战
预加载方案
在 Application.onCreate() 预初始化识别器:
class MyApp : Application() {
lateinit var warmRecognizer: SpeechRecognizer
override fun onCreate() {super.onCreate()
// 预加载避免首次调用延迟
warmRecognizer = SpeechRecognizer
.createSpeechRecognizer(this)
}
}
大数据量处理
使用 WorkManager 分片处理长语音:
class VoiceChunkWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {override suspend fun doWork(): Result {val audioFile = File(context.cacheDir, "temp.pcm")
return try {
// 分片处理逻辑
processInChunks(audioFile)
Result.success()} catch (e: OutOfMemoryError) {Result.retry()
}
}
}
避坑指南
中国区设备适配
需要检测 GMS 可用性:
fun isGmsAvailable(): Boolean {
return try {GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
} catch (e: Exception) {false}
}
连续识别保活
通过定时发送空音频包维持 Session:
private val keepAliveTimer = Timer().scheduleAtFixedRate(
initialDelay = 30000,
period = 25000
) {speechRecognizer.destroy()
speechRecognizer = createSpeechRecognizer()}
思考与延伸
尝试用 Jetpack Compose 的 Canvas API 实时绘制声波纹:
@Composable
fun VoiceWaveform(amplitude: Float) {Canvas(modifier = Modifier.fillMaxWidth().height(80.dp)) {
drawRect(
color = Color.Blue,
size = Size(width = 10f, height = amplitude * 100)
)
}
}
实际开发中,建议结合 Android 的 AudioRecord 获取原始 PCM 数据来计算实时振幅。这个方案你觉得实现难度如何?欢迎在评论区讨论你的优化思路!
正文完
