Android ASR实战:基于Vosk的离线语音识别开发指南
·
在移动应用中集成语音识别功能时,我们常常面临隐私、网络延迟和响应速度等问题。今天分享一个基于Vosk的Android离线语音识别方案,从集成到优化的完整过程,帮你绕过我踩过的那些坑。

为什么选择离线方案?
- 隐私保护:用户语音数据无需上传云端,完全在设备端处理
- 弱网可用:没有网络连接也能正常使用语音功能
- 响应更快:省去了网络传输时间,平均延迟降低300-500ms
- 成本优势:避免云服务按调用次数计费
技术选型对比
先看看主流方案的特性差异:
- Google SpeechRecognizer:
- 必须联网
- 免费但有配额限制
-
中文识别准确率高
-
ML Kit:
- 支持离线但模型较大(60MB+)
-
需要Google Play服务
-
Vosk:
- 完全离线工作
- 多语言支持(支持中文)
- 模型可裁剪(最小15MB)
- Apache 2.0开源协议
集成实战步骤
1. 环境配置
在app/build.gradle中添加依赖:
dependencies {
implementation 'net.java.dev.jna:jna:5.8.0@aar'
implementation 'com.alphacephei:vosk-android:0.3.47'
// 添加协程支持
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
}
2. 模型处理
Vosk提供了不同大小的模型,我们可以按需选择:
- 从官网下载中文小模型(约50MB)
- 使用脚本裁剪不需要的语音特征(可缩减到15MB)
- 将模型放入assets/vosk-model-zh-cn目录
3. 核心代码实现
class VoskAsrHelper(context: Context) {
private val scope = CoroutineScope(Dispatchers.IO)
private lateinit var recognizer: VoskRecognizer
private lateinit var audioRecord: AudioRecord
// NOTE: 初始化语音识别器
suspend fun initModel() = withContext(Dispatchers.IO) {
val model = Model(context.assets, "vosk-model-zh-cn")
recognizer = VoskRecognizer(model, 16000.0f)
// 音频录制配置
val bufferSize = AudioRecord.getMinBufferSize(
16000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
) * 2
audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
16000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
)
}
// NOTE: 开始实时识别
fun startListening(callback: (String) -> Unit) {
scope.launch {
val buffer = ShortArray(1024)
audioRecord.startRecording()
while (isActive) {
val len = audioRecord.read(buffer, 0, buffer.size)
if (len > 0 && ::recognizer.isInitialized) {
if (recognizer.acceptWaveForm(buffer, len)) {
val result = recognizer.result
withContext(Dispatchers.Main) {
callback(JSONObject(result).getString("text"))
}
}
}
}
}
}
fun release() {
scope.cancel()
audioRecord.release()
recognizer.close()
}
}

性能优化技巧
- 模型瘦身:
- 移除模型中不使用的语言特征
- 使用quantize.py脚本压缩模型大小
-
最终从50MB降到15MB,准确率仅下降2%
-
线程管理:
- 音频采集在IO线程进行
- 结果回调切换到主线程更新UI
-
使用协程避免回调地狱
-
内存优化:
- 限制录音缓冲池大小
- 低端设备启用GC回收提示
- 采用对象复用策略
避坑指南
-
中文乱码问题:
// 在Application中初始化时设置 System.setProperty("jna.encoding", "UTF-8") -
权限处理:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), REQUEST_AUDIO_PERMISSION) } -
低端设备适配:
- 降低采样率为8kHz
- 减少识别并发任务
- 增加内存监控回调
实测数据
在Pixel 3上测试中文识别:
- 平均延迟:320ms
- 准确率:92.4%(安静环境)
- CPU占用:<15%
- 内存增长:约25MB
扩展方向
- 结合唤醒词检测实现语音唤醒
- 使用Kaldi工具训练自定义领域模型
- 集成语义理解模块
整个项目集成下来,最大的感受是离线方案虽然需要处理更多细节,但带来的隐私保护和低延迟体验非常值得。特别是在地铁、电梯等网络不稳定的场景下,用户体验提升明显。建议先从小模型开始验证,再逐步优化到生产环境。
更多推荐


所有评论(0)