You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebView返回后再次打开无法调用相机问题求助

问题:WebView调用相机扫描二维码,返回后再次进入出现灰色播放按钮

我开发了一个加载WebView并调用相机扫描二维码的项目,但有时从页面返回后再次进入,页面会显示灰色播放按钮。模拟器中该问题出现频率较低。正常工作时Logcat输出日志如下:

W/m.migrosmagazam: Long monitor contention with owner VideoCaptureCamera2_CameraThread (6680) at void android.hardware.camera2.impl.CameraDeviceImpl.close()(CameraDeviceImpl.java:1433) waiters=0 in void android.hardware.camera2.impl.CameraDeviceImpl$CameraDeviceCallbacks.onCaptureStarted(android.hardware.camera2.impl.CaptureResultExtras, long) for 237ms
D/FA: Connected to remote service
V/FA: Processing queued up service tasks: 1
D/EGL_emulation: app_time_stats: avg=24.30ms min=3.44ms max=390.79ms count=29
D/EGL_emulation: eglCreateContext: 0x723d53444e10: maj 2 min 0 rcv 2
D/EGL_emulation: app_time_stats: avg=467.91ms min=2.15ms max=1398.04ms count=3
D/EGL_emulation: app_time_stats: avg=62.40ms min=12.81ms max=794.05ms count=17
V/FA: Inactivity, disconnecting from the service
D/EGL_emulation: app_time_stats: avg=912.88ms min=5.76ms max=6756.47ms count=8
E/WebView - Logger: Uncaught Cannnot transition to a new state, already under transition : 2 : Uncaught Cannnot transition to a new state, already under transition
E/WebView - Logger: Uncaught Cannnot transition to a new state, already under transition : 2 : Uncaught Cannnot transition to a new state, already under transition
D/EGL_emulation: eglCreateContext: 0x723d530d1550: maj 2 min 0 rcv 2
E/cr_VideoCapture: CameraDevice.StateCallback onOpened

页面显示灰色播放按钮

相关代码:

override fun onCreateFinished() {
    binding.webView.settings.javaScriptEnabled = true
    binding.webView.settings.domStorageEnabled = true
    binding.webView.settings.mediaPlaybackRequiresUserGesture = false
    binding.webView.settings.allowFileAccessFromFileURLs = true
    binding.webView.settings.allowUniversalAccessFromFileURLs = true
    binding.webView.settings.allowContentAccess = false
    binding.webView.settings.useWideViewPort = true
    binding.webView.settings.loadWithOverviewMode = true
    binding.webView.settings.cacheMode = WebSettings.LOAD_NO_CACHE
    binding.webView.loadUrl("https://izlenebilirlik.migros.com.tr/urun_izle_ms.aspx")

    if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        ErrorDialog(
            ErrorType.CUSTOM_MESSAGE,
            ::onClickWarningMessage,
            getString(R.string.you_must_allow_camera_permission_to_read_qr)
        ).show(
            childFragmentManager,
            ErrorDialog.TAG
        )
    } else {
        binding.webView.loadUrl("https://izlenebilirlik.migros.com.tr/urun_izle_ms.aspx")
    }

    binding.webView.webChromeClient = object : WebChromeClient() {
        override fun onPermissionRequest(request: android.webkit.PermissionRequest?) {
            if (request?.resources?.contains("android.permission.CAMERA") == true || request?.resources?.contains("android.webkit.resource.VIDEO_CAPTURE") == true) {
                if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    // Permission is not granted, so request it
                    ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), 100)
                } else {
                    request.grant(request.resources)
                }
            } else {
                super.onPermissionRequest(request)
            }
        }

        override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
            Log.e ("WebView - Logger", consoleMessage?.message () + " : " + consoleMessage?.lineNumber () + " : " + consoleMessage?.message ());
            return true
        }
    }
}

private fun onClickWarningMessage() {
    ActivityCompat.requestPermissions(requireActivity(), arrayOf(android.Manifest.permission.CAMERA), 100)
}
解决方案

从日志里的Uncaught Cannnot transition to a new state, already under transition错误,以及相机线程的锁竞争日志来看,问题出在相机资源没有正确释放,导致WebView再次进入时无法重新初始化相机,加上页面状态冲突引发的状态切换异常。可以按以下步骤修复:

1. 页面销毁时彻底释放WebView和相机资源

在Fragment的onDestroyView方法中,强制销毁WebView并清理相关资源,避免相机被占用:

override fun onDestroyView() {
    super.onDestroyView()
    // 停止WebView加载,释放资源
    binding.webView.stopLoading()
    binding.webView.webChromeClient = null
    binding.webView.webViewClient = null
    // 清除WebView缓存和历史
    binding.webView.clearCache(true)
    binding.webView.clearHistory()
    // 从父布局移除WebView,避免内存泄漏
    (binding.webView.parent as? ViewGroup)?.removeView(binding.webView)
    binding.webView.destroy()
}

2. 避免重复加载URL

当前代码中onCreateFinished里先调用了一次loadUrl,权限判断后又调用了一次,重复加载会导致页面状态混乱。去掉第一次的loadUrl,只在权限处理完成后加载:

override fun onCreateFinished() {
    binding.webView.settings.javaScriptEnabled = true
    binding.webView.settings.domStorageEnabled = true
    binding.webView.settings.mediaPlaybackRequiresUserGesture = false
    binding.webView.settings.allowFileAccessFromFileURLs = true
    binding.webView.settings.allowUniversalAccessFromFileURLs = true
    binding.webView.settings.allowContentAccess = false
    binding.webView.settings.useWideViewPort = true
    binding.webView.settings.loadWithOverviewMode = true
    binding.webView.settings.cacheMode = WebSettings.LOAD_NO_CACHE

    // 移除重复的loadUrl调用
    // binding.webView.loadUrl("https://izlenebilirlik.migros.com.tr/urun_izle_ms.aspx")

    if (ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        ErrorDialog(
            ErrorType.CUSTOM_MESSAGE,
            ::onClickWarningMessage,
            getString(R.string.you_must_allow_camera_permission_to_read_qr)
        ).show(
            childFragmentManager,
            ErrorDialog.TAG
        )
    } else {
        binding.webView.loadUrl("https://izlenebilirlik.migros.com.tr/urun_izle_ms.aspx")
    }

    // 剩余WebChromeClient代码不变
}

3. 处理权限申请后的页面刷新

当用户在权限申请弹窗中允许相机权限后,需要通知WebView重新加载页面,确保相机权限生效。重写onRequestPermissionsResult方法:

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 100) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限允许后重新加载页面,确保相机能正常初始化
            binding.webView.reload()
        } else {
            // 权限拒绝,再次提示用户
            ErrorDialog(
                ErrorType.CUSTOM_MESSAGE,
                ::onClickWarningMessage,
                getString(R.string.you_must_allow_camera_permission_to_read_qr)
            ).show(
                childFragmentManager,
                ErrorDialog.TAG
            )
        }
    }
}

4. 优化相机状态冲突

日志中的Long monitor contention说明相机线程有锁竞争,可在页面生命周期中暂停/恢复WebView的媒体资源使用:

override fun onPause() {
    super.onPause()
    // 暂停WebView的所有媒体资源,释放相机
    binding.webView.onPause()
    binding.webView.pauseTimers()
}

override fun onResume() {
    super.onResume()
    // 恢复WebView,重新初始化相机
    binding.webView.onResume()
    binding.webView.resumeTimers()
}

这些步骤可以解决相机资源未释放、页面重复加载、权限申请后状态不同步导致的灰色播放按钮问题,同时缓解相机线程的锁竞争情况。

内容的提问来源于stack exchange,提问作者Ahmet Yılmaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:14:59