Android 12及以上版本:WebView内嵌的WebRTC应用在锁屏或切换应用时麦克风停止工作
Hey there, I’ve run into exactly this issue with WebRTC in WebView on Android 12+ before—let’s walk through some practical workarounds that fit your constraints:
请求必要权限并调整WebView音频设置
首先,确保你的AndroidManifest.xml包含后台音频和录音所需的全部权限:<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ 必需 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <!-- Android 12+ 前台麦克风访问专用 -->初始化WebView时,配置它允许后台无间断的音频和麦克风访问:
val webSettings = webView.settings webSettings.mediaPlaybackRequiresUserGesture = false // 跳过媒体启动的用户手势要求 webSettings.allowContentAccess = true webSettings.allowFileAccess = true // 请求持久音频焦点,防止后台被中断 val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager val audioAttributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build() val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(audioAttributes) .setAcceptsDelayedFocusGain(true) .setOnAudioFocusChangeListener { focusChange -> // 失去焦点时重新请求 if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { audioManager.requestAudioFocus(audioFocusRequest) } } audioManager.requestAudioFocus(audioFocusRequest)用JavaScript接口打通WebView与原生代码
你可以创建一个轻量的原生接口,让PeerJS代码在通话开始/结束时通知Android层。这样就能启动一个最小化的前台服务(Android 12+后台录音必需),同时不干扰WebView的逻辑:class WebAudioInterface(private val activity: Activity) { @JavascriptInterface fun onCallStarted() { // 启动前台服务维持后台麦克风访问 val serviceIntent = Intent(activity, AudioForegroundService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { activity.startForegroundService(serviceIntent) } else { activity.startService(serviceIntent) } } @JavascriptInterface fun onCallEnded() { // 通话结束后停止服务 val serviceIntent = Intent(activity, AudioForegroundService::class.java) activity.stopService(serviceIntent) } } // 将接口绑定到WebView webView.addJavascriptInterface(WebAudioInterface(this), "AndroidAudioHandler")在Web端的PeerJS代码里,通话状态变化时调用这些方法:
// 通话连接成功时 peer.on('call', (call) => { call.answer(localStream); window.AndroidAudioHandler.onCallStarted(); }); // 通话结束时 call.on('close', () => { window.AndroidAudioHandler.onCallEnded(); });前台服务本身可以做得非常极简,只要满足Android的后台音频规则就行:
class AudioForegroundService : Service() { override fun onCreate() { super.onCreate() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId = "voice_call_channel" val channel = NotificationChannel(channelId, "Voice Call", NotificationManager.IMPORTANCE_LOW) val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) val notification = NotificationCompat.Builder(this, channelId) .setContentTitle("Voice Call Active") .setContentText("Tap to return to your call") .setSmallIcon(R.drawable.ic_call) // 使用你自己的通话图标 .setPriority(NotificationCompat.PRIORITY_LOW) .build() startForeground(1, notification) } } override fun onBind(intent: Intent): IBinder? { return null } }不用在意这个通知——设置为
IMPORTANCE_LOW后,它不会弹窗或发出声音,只是告诉Android你的应用正在合法使用音频。优化Activity生命周期处理
通过调整Activity的生命周期方法,防止WebView在应用后台时被暂停:override fun onPause() { super.onPause() // 确保WebView在后台继续运行 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { webView.onResume() // 覆盖默认的暂停WebView的onPause行为 } // 将应用移至后台而不终止进程 moveTaskToBack(true) }同时,在
AndroidManifest.xml中给你的CallScreen Activity添加这些属性,提升它的后台优先级:<activity android:name=".CallScreenActivity" android:launchMode="singleTop" android:showOnLockScreen="true" <!-- 可选:允许用户在锁屏时操作通话 --> android:excludeFromRecents="false" />
核心思路是:前台服务和WebView并不冲突,它只是一个“权限包装器”,让Android认可你的应用音频使用是合法的。通过打通Web层与原生层,你可以继续在WebView中运行PeerJS逻辑,同时满足Android 12+的后台音频限制。
备注:内容来源于stack exchange,提问作者Sujith S Manjavana




