Composable嵌入WebView切换底部导航栏时触发Fatal signal 11崩溃
解决Composable中WebView切换标签页崩溃问题
问题场景
我在Composable中嵌入了WebView,未在Activity代码中直接使用该WebView,仅让其在Composable中显示。应用底部导航栏包含A、B、C、D四个标签页,WebView位于A标签页。当从A标签页切换至D标签页,再切回A标签页时,应用崩溃,抛出以下错误:
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x20 in tid 23934 (RenderThread), pid 23898 (.mobile.android) 12-08 19:29:21.885 22765 22765 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000020 12-08 19:29:21.885 22765 22765 F DEBUG : Cause: null pointer dereference 12-08 19:29:21.885 22765 22765 F DEBUG : x0 0000000000000000 x1 0000000000000002 x2 0000000000000000 x3 0000000000000001 12-08 19:29:21.885 22765 22765 F DEBUG : x4 0000000000000001 x5 0000000000000000 x6 0000000000000001 x7 0000000000000001 12-08 19:29:21.885 22765 22765 F DEBUG : x8 aeeabf0bf3658480 x9 aeeabf0bf3658480 x10 00000076cb15c000 x11 000000000000006d 12-08 19:29:21.885 22765 22765 F DEBUG : x12 0000000000000004 x13 0000000000000001 x14 0000000000000000 x15 0000000000000000 12-08 19:29:21.885 22765 22765 F DEBUG : x16 0000000000000001 x17 0000007a4dbb2ec0 x18 00000076ca0be000 x19 0000000000000000 12-08 19:29:21.885 22765 22765 F DEBUG : x20 0000000000000000 x21 00000076cb151740 x22 0000000000000000 x23 0000000000000000 12-08 19:29:21.885 22765 22765 F DEBUG : x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 00000077ce2ffb90 12-08 19:29:21.885 22765 22765 F DEBUG : x28 0000000000000000 x29 00000076cb151630 12-08 19:29:21.885 22765 22765 F DEBUG : lr 0000007a5bd18300 sp 00000076cb151630 pc 0000007a5be95b90 pst 0000000060000000 12-08 19:29:21.885 22765 22765 F DEBUG : backtrace: 12-08 19:29:21.885 22765 22765 F DEBUG : #00 pc 0000000000695b90 /system/lib64/libhwui.so (SkSurface::getCanvas()+16) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #01 pc 00000000005182fc /system/lib64/libhwui.so (android::uirenderer::skiapipeline::GLFunctorDrawable::onDraw(SkCanvas*)+860) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #02 pc 00000000005a5634 /system/lib64/libhwui.so (SkDrawable::draw(SkCanvas*, SkMatrix const*)+116) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #03 pc 00000000008d6bd4 /system/lib64/libhwui.so (skgpu::v1::Device::drawDrawable(SkCanvas*, SkDrawable*, SkMatrix const*)+344) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #04 pc 00000000004f7d70 /system/lib64/libhwui.so (android::uirenderer::$_37::__invoke(void const*, SkCanvas*, SkMatrix const&)+136) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #05 pc 00000000004f4f54 /system/lib64/libhwui.so (android::uirenderer::DisplayListData::draw(SkCanvas*) const+128) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #06 pc 00000000004de5b8 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::drawContent(SkCanvas*) const+1748) (BuildId: 5e7ceec4d8844b047ab06d2f7ae9c6c5) 12-08 19:29:21.885 22765 22765 F DEBUG : #07 pc 00000000004dece4 /system/lib64/libhwui.so (android::uirenderer::skiapipeline::RenderNodeDrawable::forceDraw(SkCanvas*)
问题代码
@Composable private fun TestWebView(url: String, onReceivedError: (WebResourceError) -> Unit) { val scrollState = rememberScrollState() var mWebView: WebView? = null // var webView: WebView? by remember { mutableStateOf(null) } // State to track whether the WebView is loading or not var isLoading by remember { mutableStateOf(true) } Box( modifier = Modifier.fillMaxSize() ) { AndroidView( modifier = Modifier .fillMaxWidth() .verticalScroll(scrollState), factory = { context -> WebView(context).apply { mWebView = WebView(context); webViewClient = object : WebViewClient() { override fun onReceivedError( view: WebView?, request: WebResourceRequest?, error: WebResourceError? ) { super.onReceivedError(view, request, error) error?.let { onReceivedError(it) } isLoading = false } override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? ): Boolean { view?.loadUrl(request?.url.toString()) return true } override fun onRenderProcessGone( view: WebView?, detail: RenderProcessGoneDetail? ): Boolean { return false } override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) isLoading = true } override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) isLoading = false } } settings.domStorageEnabled = true settings.javaScriptEnabled = true settings.cacheMode = WebSettings.LOAD_DEFAULT this.alpha = 0.99F loadUrl(url) } }, update = { webView -> webView.loadUrl(url) } ) if (isLoading) { CircularProgressIndicator( modifier = Modifier .size(30.dp) .align(Alignment.Center) ) } // Dispose the WebView to avoid memory leaks when the composable is removed DisposableEffect(Unit) { onDispose { mWebView?.clearCache(true); mWebView?.clearHistory(); mWebView?.onPause(); mWebView?.removeAllViews(); mWebView?.destroy(); mWebView = null } } } }
问题原因
- 重复创建WebView实例:在
AndroidView的factory中,实际渲染的是WebView(context).apply{...}创建的实例,但你又额外通过mWebView = WebView(context)创建了一个新实例。导致DisposableEffect销毁的是未绑定到Composable的WebView,真正显示的WebView未被正确销毁,切换标签页后系统尝试渲染已失效的资源,引发RenderThread空指针崩溃。 - 未正确保存WebView引用:
mWebView没有用remember修饰,每次Composable重组都会被重置为null,可能导致DisposableEffect无法获取有效实例进行销毁。 - 不必要的重复加载:
update块每次都会调用loadUrl(url),即使URL未变化,引发不必要的状态波动。
修复方案
1. 修正WebView实例创建与引用
将mWebView改为用remember保存的可变状态,直接绑定AndroidView创建的实例,删除重复创建代码:
var mWebView: WebView? by remember { mutableStateOf(null) }
在factory的apply块中设置mWebView = this(this即为当前创建的WebView实例)。
2. 优化update块逻辑
仅当URL变化时才重新加载:
update = { webView -> if (webView.url != url) { webView.loadUrl(url) } }
3. 调整DisposableEffect依赖
让DisposableEffect依赖mWebView,确保仅当WebView存在时执行销毁操作:
DisposableEffect(mWebView) { onDispose { mWebView?.let { webView -> webView.clearCache(true) webView.clearHistory() webView.onPause() webView.removeAllViews() webView.destroy() mWebView = null } } }
修复后的完整代码
@Composable private fun TestWebView(url: String, onReceivedError: (WebResourceError) -> Unit) { val scrollState = rememberScrollState() var mWebView: WebView? by remember { mutableStateOf(null) } var isLoading by remember { mutableStateOf(true) } Box(modifier = Modifier.fillMaxSize()) { AndroidView( modifier = Modifier .fillMaxWidth() .verticalScroll(scrollState), factory = { context -> WebView(context).apply { mWebView = this webViewClient = object : WebViewClient() { override fun onReceivedError( view: WebView?, request: WebResourceRequest?, error: WebResourceError? ) { super.onReceivedError(view, request, error) error?.let { onReceivedError(it) } isLoading = false } override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? ): Boolean { view?.loadUrl(request?.url.toString()) return true } override fun onRenderProcessGone( view: WebView?, detail: RenderProcessGoneDetail? ): Boolean { return false } override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) isLoading = true } override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) isLoading = false } } settings.domStorageEnabled = true settings.javaScriptEnabled = true settings.cacheMode = WebSettings.LOAD_DEFAULT alpha = 0.99F loadUrl(url) } }, update = { webView -> if (webView.url != url) { webView.loadUrl(url) } } ) if (isLoading) { CircularProgressIndicator( modifier = Modifier .size(30.dp) .align(Alignment.Center) ) } DisposableEffect(mWebView) { onDispose { mWebView?.let { webView -> webView.clearCache(true) webView.clearHistory() webView.onPause() webView.removeAllViews() webView.destroy() mWebView = null } } } } }
内容的提问来源于stack exchange,提问作者prat
相关产品推荐
相关产品推荐

