H5端必须用agora-rtc-sdk-ng,App端必须用声网官方UniApp插件(如Agora-RTC_3.7.2),不可混用;否则iOS静音、Android黑屏、H5报NotAllowedError或不触发user-published回调。

直接说结论:H5端用 agora-rtc-sdk-ng,App端(Android/iOS)必须用声网官方提供的 UniApp 插件(如 Agora-RTC_3.7.2),不能混用 Web SDK 或原生 SDK 手动桥接——否则 iOS 会静音、Android 会黑屏、H5 会报 NotAllowedError 或进不了 user-published 回调。
为什么 H5 必须用 agora-rtc-sdk-ng 而不是旧版 agora-rtc-sdk
新版 SDK(agora-rtc-sdk-ng)是声网 2021 年后主推的 TypeScript 重构版本,对 Promise、async/await、错误分类(如 DEVICE_NOT_FOUND、NOT_ALLOWED_ERROR)做了明确区分;旧版 SDK 在 Vue3 + Composition API 下容易出现 client.join 返回 undefined 或订阅后 remoteAudioTrack.play() 无声的问题。H5 环境下,浏览器策略(如 Autoplay Policy)要求音频必须由用户手势触发,agora-rtc-sdk-ng 的 play() 方法内部已做兼容处理,而旧版需手动加 mute/unmute 绕过。
- 安装命令必须是:
npm install agora-rtc-sdk-ng(不是agora-rtc-sdk) - 初始化 client 时,
mode: 'rtc'和codec: 'h264'必须显式指定,否则 Safari 17+ 可能拒绝解码 - H5 页面首次加载后,必须等用户点击按钮(如“加入通话”)再调用
client.join(),否则 Chrome 会静音且不报错
Android/iOS 离线打包为何不能用云打包 + npm 插件
UniApp 官方云打包会剥离所有原生插件逻辑,包括声网所需的 AudioManager 权限申请、后台音频保活、摄像头硬件加速控制。实测发现:云打包后的 APK 在 Android 12+ 上默认禁用麦克风后台采集,iOS 则因 AVAudioSession 配置缺失导致进房后秒退。必须用离线自定义基座(Custom Base SDK)方式集成。
- Android 侧需把声网提供的
agora-rtc-android.aar放入nativeplugins/agora-rtc/android/libs/,并在android/app/build.gradle中添加useLibrary 'android.test.runner' - iOS 侧需在 Xcode 工程中手动 link
AgoraRtcEngineKit.xcframework,并确保Info.plist包含NSMicrophoneUsageDescription和NSCameraUsageDescription -
uni-app 项目里不能 import 任何
agora-rtc-sdk-ng相关代码到platforms/app-plus/目录下,否则离线编译会报Cannot resolve module 'agora-rtc-sdk-ng'
user-published 回调不触发的三个真实原因
这个回调进不去,90% 不是代码问题,而是服务层或环境限制。常见于开发调试阶段,但文档极少明说。
- 声网控制台「每日免费额度」用完(尤其测试频繁时),
client.join()表面成功,但后续所有事件(包括user-published)全部静默,日志里也无错误提示 - Token 过期或签发时
channelName与 join 时传入的不一致,会导致远端用户根本无法发布音轨,自然不会触发该回调 - H5 端用的是非 HTTPS 协议(如 localhost:8080),Chrome 会禁止
getUserMedia,本地音轨创建失败,进而导致整个信令链路中断,user-published永远不会来
Vue3 + TS 封装时最容易漏掉的生命周期控制
在 onUnmounted 里只调用 client.leave() 是不够的。声网 SDK 的音轨对象(localAudioTrack、remoteAudioTrack)持有 DOM 引用和 MediaStream,不手动 stop/release,H5 页面切走后仍占用麦克风,Android 端会持续消耗 CPU,iOS 则可能被系统 kill。
- 必须在组件卸载前依次执行:
localAudioTrack?.stop()→localAudioTrack?.close()→client.leave() - 远端音轨播放后,若未显式调用
remoteAudioTrack.stop(),下次进房时play()可能失败并抛出DOMException: The play() request was interrupted - 不要在
onBeforeUnmount里做异步等待(如 await client.leave()),该钩子不支持 Promise,应改用onUnmounted+setTimeout延迟释放
真正麻烦的从来不是 API 调用顺序,而是三端权限模型、浏览器 Autoplay 策略、声网 Token 生效范围、以及离线基座里 aar / xcframework 的符号链接是否正确——这些地方一错,现象都是“没声音”或“黑屏”,但根因完全不同。











