更新记录

1.1.0(2026-09-29)

  • 新增 App-iOS 支持:基于讯飞 AIUI 的 ivw71 单麦唤醒引擎,唤醒词由平台下载的 res.bin 资源固定提供(港漫港漫 / 可乐可乐)。
  • setup() 新增可选字段 apiKey、apiSecret、wakeupMode、scene(仅 iOS 使用);solutionType 新增 MIC1_STD_3177。
  • iOS 下前台服务模式自动降级为普通监听,动态唤醒词 API 返回错误码 9021999(AIUI 未开放端侧唤醒词增删)。
  • 已在 iPhone 15 真机完成 uni-app / uni-app x 双链路验证:唤醒命中、连续唤醒、录音交接、锁屏后台命中。

平台兼容性

uni-app(4.87)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ - - √ - √ √ -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(4.87)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - √ √ - -

hans-vtn-wake

语音唤醒插件。Android 基于讯飞 VTN SDK,提供页面内监听、前台服务监听、唤醒事件回调和动态唤醒词资源管理;iOS 基于讯飞 AIUI 的 ivw71 单麦唤醒引擎,唤醒词由平台下载资源固定提供。

支持平台

  • 支持:uni-app / uni-app x 的 App-Android、App-iOS
  • 不支持:H5、各类小程序、Harmony

接入要求

  • HBuilderX >= 4.87.0
  • Android minSdkVersion >= 24
  • 需使用 Android / iOS 自定义基座或正式打包产物验证,标准基座下原生依赖和资源不会生效
  • iOS 只能真机验证:iflyAIUI.framework 未提供 arm64 模拟器切片,模拟器链接必然失败

使用前准备

  • 页面统一从 @/uni_modules/hans-vtn-wake 导入,不要直接引用 utssdk/*
  • 业务侧需要准备可用的 appId、sn(iOS 另可选传 apiKey / apiSecret)
  • Android 的 resIdentifier 必须和插件内资源目录一致,例如 assets/res/ivw_3.17.12/gwgw/
  • solutionType 可省略,插件按平台自动判定:Android 为 MIC1_STD_31712,iOS 为 MIC1_STD_3177
  • 运行时仍需申请 RECORD_AUDIO;如果使用前台服务模式,Android 13+ 还需要通知权限

导入

import {
  checkPermission,
  requestPermission,
  setup,
  startListening,
  stopListening,
  startForegroundListening,
  stopForegroundListening,
  destroy,
  getState,
  onWakeup,
  offWakeup,
  onAuth,
  offAuth,
  generateWakeWords,
  addWakeWords,
  removeWakeWords,
  setLogEnabled,
  isLogEnabled
} from '@/uni_modules/hans-vtn-wake'

快速开始

页面模式

适合页面停留期间做语音唤醒,页面离开后手动停止。

import {
  checkPermission,
  requestPermission,
  setup,
  startListening,
  stopListening,
  destroy,
  onWakeup,
  offWakeup,
  onAuth,
  offAuth
} from '@/uni_modules/hans-vtn-wake'

let wakeupListenerId: number | null = null
let authListenerId: number | null = null

async function startPageWakeup() {
  const granted = checkPermission() || await requestPermission()
  if (!granted) {
    throw new Error('录音权限未授予')
  }

  await setup({
    appId: '你的 appId',
    sn: '设备唯一 sn',
    resIdentifier: 'gwgw',
    workDirName: 'iflytek',
    enableLog: true,
  })

  wakeupListenerId = onWakeup((event) => {
    console.log('wakeup', JSON.stringify(event))
  })

  authListenerId = onAuth((event) => {
    console.log('auth', JSON.stringify(event))
  })

  await startListening()
}

async function stopPageWakeup() {
  try {
    await stopListening()
  } finally {
    if (wakeupListenerId != null) {
      offWakeup(wakeupListenerId)
      wakeupListenerId = null
    }
    if (authListenerId != null) {
      offAuth(authListenerId)
      authListenerId = null
    }
    destroy()
  }
}

推荐顺序:

  1. checkPermission() / requestPermission()
  2. setup()
  3. onWakeup() / onAuth()
  4. startListening()
  5. stopListening()
  6. destroy()

前台服务模式

适合应用切到后台后继续保持唤醒监听。前台服务模式和页面模式互斥,切换前先停掉另一种模式。

import {
  checkPermission,
  requestPermission,
  setup,
  startForegroundListening,
  stopForegroundListening,
  getState,
  onWakeup,
  offWakeup
} from '@/uni_modules/hans-vtn-wake'

let wakeupListenerId: number | null = null

async function startServiceWakeup() {
  const granted = checkPermission() || await requestPermission()
  if (!granted) {
    throw new Error('录音权限未授予')
  }

  await setup({
    appId: '你的 appId',
    sn: '设备唯一 sn',
    resIdentifier: 'gwgw',
    workDirName: 'iflytek',
  })

  wakeupListenerId = onWakeup((event) => {
    console.log('wakeup', JSON.stringify(event))
  })

  await startForegroundListening({
    serviceTitle: '语音唤醒服务',
    serviceText: '正在后台监听语音唤醒',
  })

  console.log('state', JSON.stringify(getState()))
}

async function stopServiceWakeup() {
  await stopForegroundListening()
  if (wakeupListenerId != null) {
    offWakeup(wakeupListenerId)
    wakeupListenerId = null
  }
}

说明:

  • startForegroundListening() 前仍然需要先 setup(),服务会读取最近一次保存的初始化参数
  • stopForegroundListening() 只停止前台服务模式,不会清理页面上注册的事件监听
  • 如果前台服务仍在运行时调用 destroy(),插件会把前台服务一并停止
  • iOS 上调用 startForegroundListening() 会自动降级为普通监听(iOS 无前台服务),后台保活依赖 UIBackgroundModes: audio

状态与事件

getState()

可读取当前插件运行状态,常用字段:

  • initialized: 是否已完成 setup()
  • listening: 当前是否正在采集音频
  • runMode: page | foreground-service
  • serviceRunning: 前台服务是否仍在运行
  • notificationReady: 前台服务通知是否已就绪
  • lastError: 最近一次错误信息
  • workDir、solutionType、resIdentifier
  • micCount、refCount、frameBytes

onWakeup(callback)

唤醒事件常用字段:

  • raw: 原始回调字符串
  • timestamp: 事件时间戳
  • keyword: 命中的唤醒词
  • keywordType
  • startMs / endMs
  • beam / physical
  • score / threshold
  • power / angle / snr

onAuth(callback)

鉴权事件常用字段:

  • raw
  • timestamp
  • result
  • authType

监听移除

  • offWakeup(listenerId) / offAuth(listenerId):移除指定监听器
  • offWakeup() / offAuth():不传参数时清空当前类型的全部监听器

iOS 说明

与 Android 的能力差异:

能力 Android(VTN) iOS(AIUI)
资源标识 resIdentifier(如 gwgw) 由 res.bin 文件本身决定,resIdentifier 仅回显
平台凭据 appId + sn appId + sn,另可传 apiKey / apiSecret
后台常驻 前台服务 无前台服务,依赖 UIBackgroundModes: audio
唤醒词管理 支持端侧生成/增删 不支持,唤醒词由平台下载资源固定
await setup({
  appId: '你的 appId',
  sn: '设备唯一 sn',
  resIdentifier: 'gwgw',              // iOS 仅回显,不参与资源定位
  apiKey: 'AIUI login.key',           // 可选
  apiSecret: 'AIUI login.api_secret', // 可选
})

注意:

  • 不要把真实的 apiKey / apiSecret 提交到仓库,请在运行时由业务侧注入
  • 首次鉴权需要联网(AIUI 会按 appId + sn 做装机量鉴权并缓存),此后可离线唤醒;装机量不足会报 10408 / 600022
  • iOS 命中后 SDK 会自动复位为可唤醒状态,可连续唤醒,无需手动处理
  • 退到后台或锁屏后能否继续唤醒,取决于系统是否回收进程,没有 Android 前台服务级别的保活保证;真机实测(iPhone 15 / iOS 26)声明 UIBackgroundModes: audio 后锁屏与后台可继续唤醒命中

变更 iOS 唤醒词

唤醒词来自 AIUI 平台按你的 appId 下载的 3.17.7 资源包,当前内置 港漫港漫 与 可乐可乐。如需变更:

  1. 在 AIUI 平台重新生成资源,替换 utssdk/app-ios/Resources/res.bin(引擎版本必须仍是 3.17.7,不匹配会报 600103)
  2. 同步更新 utssdk/app-ios/index.uts 里 wakeWordText() 的拼音→中文映射,否则唤醒事件里的 keyword 会回落成拼音原串

动态唤醒词

动态唤醒词相关 API 需要在 setup() 成功之后调用,仅 Android 可用。

import {
  setup,
  generateWakeWords,
  addWakeWords,
  removeWakeWords
} from '@/uni_modules/hans-vtn-wake'

let resourceId: number | null = null

async function updateWakeWords() {
  await setup({
    appId: '你的 appId',
    sn: '设备唯一 sn',
    resIdentifier: 'gwgw',
  })

  const generated = await generateWakeWords({
    keywords: '小飞小飞|你好小飞',
  })

  const added = await addWakeWords({
    resourcePath: generated.outputPath,
  })

  resourceId = added.resourceId
}

async function clearWakeWords() {
  if (resourceId == null) {
    return
  }
  await removeWakeWords({ resourceId })
  resourceId = null
}

说明:

  • generateWakeWords({ keywords }) 中的 keywords 为唤醒词文本,多个词可按你的业务格式拼接后传入
  • 未传 outputPath 时,插件会把生成结果写到工作目录下的 userKeywordResource/generated_wakeup_words.bin
  • addWakeWords() 返回的 resourceId 需要自行保存,后续通过 removeWakeWords({ resourceId }) 卸载
  • iOS 上以上三个 API 返回错误码 9021999

日志

  • setLogEnabled(true):打开插件日志
  • isLogEnabled():读取当前日志开关

建议在联调阶段主动开启日志,发布前再按业务需要决定默认值。

使用建议

  • setup() 的 sn 应该是设备唯一值,不要对所有设备写死同一个值
  • 页面模式和前台服务模式不要并行启动,同一时刻只保留一种模式
  • 前台服务模式请重点验证通知权限、锁屏、后台切换和重复启停场景
  • 如果只是切换页面但希望后台继续监听,不要误调 destroy()
  • 如果需要彻底释放插件,包括停止前台服务,再调用 destroy()

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android 需要 RECORD_AUDIO、INTERNET、ACCESS_NETWORK_STATE、ACCESS_WIFI_STATE、CHANGE_NETWORK_STATE、MODIFY_AUDIO_SETTINGS;iOS 需要麦克风权限(NSMicrophoneUsageDescription)与 audio 后台模式。接入前需由业务侧自行提供合法的 appId、sn 及 VTN/AIUI 资源配置;iOS 的 apiKey / apiSecret 必须在运行时传入,不要提交到仓库。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件仅在本地进行录音、鉴权与唤醒词检测,不包含额外的数据采集逻辑。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无广告

暂无用户评论。