背景:
App主页面使用了大量WebView容器(10个以上)显示图表信息,最新发现bugly上面出现一些关于浏览器Native Crash,如下:
经排查,是WebView渲染失败导致Crash,可以通过webView.loadUrl("chrome://crash")模拟。
解决方法:
1、通过设置WebViewClient,重写onRenderProcessGone()返回值,强制返回true,表示在WebView发生异常时,自己处理,这样App就不会出现Crash。这么做App虽然没有Crash,但是主页面的WebView内容却看不到了,看到的是白色/黑色背景,体验极差。
2、要想解决WebView内容不可见问题,还需要在Web出现异常的时候,移除原有Web容器,重新创建一个Web容器,代码如下:
class ReportWebView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : wendu.dsbridge.BaseWebView(context, attrs) {
var reloadFun: ((any: ReportWebView) -> Unit)? = null
private var parentViewGroup: ViewGroup? = null
init {
webViewClient = CustomWebViewClient()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
parentViewGroup = parent as? ViewGroup
}
private inner class CustomWebViewClient : WebViewClient() {
override fun onRenderProcessGone(view: WebView?, detail: RenderProcessGoneDetail?): Boolean {
// 所有的web都crash,所以都需要重建
recreateWebViewAndReload(view)
return true
}
}
private fun recreateWebViewAndReload(view: WebView?) {
val originalUrl = view?.url// 原始webView地址
val isVisible = view?.isVisible
val lp = this.layoutParams
// 移除旧的 WebView
val index = indexInParent()
if (parentViewGroup != null) {
parentViewGroup?.removeView(this)
}
destroy()// 销毁
// 重新创建 WebView
val newWebView = ReportWebView(context)
reloadFun?.invoke(newWebView)
newWebView.reloadFun = reloadFun
newWebView.id = id
newWebView.layoutParams = lp
newWebView.isVisible = isVisible.nullOr(false)
originalUrl?.let { newWebView.loadUrl(it) }
// 将新的 WebView 添加回布局中
parentViewGroup?.addView(newWebView, index)
// 更新引用
parentViewGroup = newWebView.parent as? ViewGroup
}
private fun indexInParent(): Int {
return parentViewGroup?.indexOfChild(this) ?: -1
}
}
本项目桥接使用的是DSBridge三方库,在创建Web容器需要设置addJavascriptObject(),即reloadFun函数。
注意事项:
1、同一个页面只要有一个渲染异常,会导致所有Web容器异常,所以所有Web容器都要重新创建,不可以根据Web可见状态只创建可见的Web。
2、在使用的时候,原有的Web容器已被移除,需要使用最新的Web容器,否则就会报错。上述代码中,新的Web容器id跟移除的一样,所以也很容易拿到新的Web容器,代码如下:
/**
* 获取真实的webView,之前的web可能被销毁
* @param id web id
*/
private fun getRealWebView(id: Int): ReportWebView {
return mBinding.root.findViewById(id)
}