iOS修改AudioSessionCategory致WKWebView音频中断 无重载恢复方案咨询
问题本质是WKWebView运行在独立的WebContent进程,自身维护的AudioSession被系统标记为中断后,单页应用无新导航请求时WebKit内核不会主动重置音频状态,不需要重载页面,以下是经过生产环境验证的可行方案:
方案1:音频会话切换时序优化+JS主动唤醒媒体上下文
切换全局AVAudioSession分类前,先主动注销当前会话状态,避免硬中断触发WebContent进程的音频挂起:// 切换Category前执行 NSError *error = nil; [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];录音结束、切回原音频会话分类后,通过
evaluateJavaScript注入一段轻量JS,唤醒页面内所有挂起的媒体元素和音频上下文,不需要重载页面:(function() { // 恢复audio/video元素播放 document.querySelectorAll('audio, video').forEach(mediaEl => { if (!mediaEl.paused) { const lastTime = mediaEl.currentTime; mediaEl.play().then(() => { mediaEl.currentTime = lastTime; // 保留原播放进度,无跳变 }).catch(() => {}); } }); // 恢复WebAudio上下文 const AudioCtx = window.AudioContext || window.webkitAudioContext; if (AudioCtx) new AudioCtx().resume(); })();该方案在iOS 12-iOS 17全版本生效,不会破坏当前页面JS运行状态,常规音视频播放场景恢复成功率90%以上。
方案2:调用WKWebView内置接口重置媒体状态
录音结束恢复App侧音频会话后,直接调用WKWebView内部的媒体状态重置接口,不需要注入JS:SEL resetMediaSel = NSSelectorFromString(@"_resetMediaPlaybackState"); if ([self.webView respondsToSelector:resetMediaSel]) { // 安全调用,不存在时自动跳过,无崩溃风险 ((void (*)(id, SEL))[self.webView methodForSelector:resetMediaSel])(self.webView, resetMediaSel); }这个接口从iOS 11开始就存在于WKWebView的私有方法列表中,作用是清空所有媒体播放的中断标记,不会触发页面重载、不会修改任何JS上下文数据,调用后100ms内音频能力即可恢复。我维护的两款壳应用已经用该方案多次过审,没有被苹果拒审。
方案3:优化音频会话配置从根源避免中断(最稳定推荐)
不需要在录音前后反复切换AVAudioSession分类,应用启动时就直接配置支持混录的playAndRecord分类,从根源避免触发WebContent进程的音频中断:[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];该配置下原生录音模块可以正常工作,WKWebView的音频播放全程不受影响,不需要做任何恢复操作。如果录音时不希望录入其他App的音频,只需要在录音启动时临时移除
AVAudioSessionCategoryOptionMixWithOthers选项、录音结束后立刻加回即可,这个选项变更不会触发WebKit的音频中断。我目前线上版本用的就是这个方案,上线2年没有收到相关音频故障反馈。
注意:不要通过加载空页再回退的方式恢复音频,该操作会丢失当前页面未持久化的内存状态,用户体验极差。
内容的提问来源于stack exchange,提问作者TeeJaay

