You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 12及以上版本:WebView内嵌的WebRTC应用在锁屏或切换应用时麦克风停止工作

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

火山引擎 最新活动