什么是 WebChromeClient 和 WebViewClient?它们各自的职责是什么?
解读
在国内 Android 面试中,WebView 相关问题是“性能优化 + 安全合规”双热点。
面试官问这一对 Client,通常想验证三件事:
- 你是否真的用过 WebView,而不是“套壳浏览器”;
- 你是否能把“网页事件”与“原生行为”正确解耦;
- 你是否知道如何绕过国内ROM(华为、小米、OPPO、vivo)对 WebView 内核的魔改坑点。
回答时务必先给出“官方定义 → 职责边界 → 国内坑点 → 实战场景”,否则会被追问“那你怎么处理微信 x5 内核白屏?”
知识点
- WebViewClient:位于 android.webkit 包,负责渲染层事件,生命周期与 URL 重定向全部跑在 UI 线程;关键回调:shouldOverrideUrlLoading、onPageStarted、onPageFinished、onReceivedError、shouldInterceptRequest(国内厂商 5.0+ 以上用来替换资源)。
- WebChromeClient:同样位于 android.webkit,负责UI 装饰层事件,跑在 UI 线程但可弹出系统级窗口;关键回调:onProgressChanged、onReceivedTitle、onJsAlert/onJsConfirm/onJsPrompt、onShowCustomView(全屏视频)、onGeolocationPermissionsShowPrompt(H5 定位)。
- 两者均为接口适配器,WebView 内部持有 WebViewClient 与 WebChromeClient 的弱引用,允许为 null;若同时设置,WebView 会分别回调。
- 国内合规:工信部 164 号文要求 WebView 必须支持 x5/系统内核双降级,因此需在 shouldInterceptRequest 里做“内核不一致”兜底;同时隐私合规需在 onGeolocationPermissionsShowPrompt 中弹“隐私协议”弹窗,否则应用市场审核直接打回。
- 性能陷阱:shouldOverrideUrlLoading 中做重定向拦截时,若直接 return true 后没调用 view.loadUrl,会在部分 8.0 ROM 出现“空白页 onPageFinished 不回调”Bug;官方推荐采用 WebResourceRequest.isRedirect+URLUtil 判断,再同步 post loadUrl。
答案
WebChromeClient 与 WebViewClient 是 WebView 暴露的两组回调接口,用来把“网页世界”的事件同步给“原生世界”。
WebViewClient 职责:
- 拦截并决定是否重定向 URL(shouldOverrideUrlLoading),是“单页面路由”与“混合跳转”的核心;
- 监听页面生命周期:onPageStarted/onPageFinished 可配合进度条、骨架屏;
- 处理渲染错误:onReceivedError/onReceivedHttpError 可统一跳本地错误页,避免 404 白屏;
- 资源拦截:shouldInterceptRequest 可替换 http 为本地缓存包,实现“离线包”方案,国内大厂普遍用于小程序内核;
- SSL 握手:onReceivedSslError 若直接 handler.proceed() 会被应用商店认定为“高危”,必须弹窗让用户选择。
WebChromeClient 职责:
- 进度与标题:onProgressChanged 驱动顶部进度条,onReceivedTitle 设置 Toolbar 标题;
- JS 弹窗三兄弟:onJsAlert/onJsConfirm/onJsPrompt,如不覆写,系统默认用 1998 年样式的灰色对话框,用户体验极差;
- 全屏播放:onShowCustomView/onHideCustomView 是视频全屏的核心,需配合 Activity 横屏与 DecorView addView;
- 定位权限:onGeolocationPermissionsShowPrompt 需在 Manifest 声明 ACCESS_FINE_LOCATION,并在回调中调用 callback.invoke(origin, true, false) 授权,否则高德/百度地图 H5 版拿不到经纬度;
- 文件选择:onShowFileChooser 是 H5 上传图片的唯一入口,Android 11+ 需用 ActivityResultLauncher 处理获取 URI 并 grantUriPermission。
一句话总结:WebViewClient 管“网页怎么加载”,WebChromeClient 管“网页怎么跟用户交互”。
拓展思考
- 双内核降级:国内项目需在 Application 中反射调用 QbSdk.initX5Environment,若 x5 加载失败则在 shouldInterceptRequest 里把本地 v8 内核 so 路径注入,确保 32 位老机型不崩溃。
- 安全隔离:对第三方业务 H5,应新建独立 WebView 进程,AndroidManifest 中给 activity 设置 android:process=":web",并在 shouldInterceptRequest 里禁止 file:// 协议,防止 file 跨域。
- 内存泄漏:WebView 持有 Activity 引用,必须在 onDestroy 中先调用 webView.stopLoading()、webView.setWebViewClient(null)、webView.setWebChromeClient(null)、webView.destroy(),再从 ViewGroup remove;否则华为机型会出现 InputMethodManager 内存泄漏警告。
- 性能监控:利用 WebViewClient.onRenderProcessGone 回调,可统计内核崩溃率,结合 Firebase/友盟上报,若崩溃率>0.3% 则触发动态降级到系统内核。
- Compose 时代:Accompanist-Web 组件把 WebView 包成 AndroidView,但底层仍依赖 WebViewClient/WebChromeClient; Jetpack 目前未提供官方 Compose 版,因此面试中可主动提出“用 rememberSaveable 保存 WebViewState,用 LaunchedEffect 监听 onPageFinished”作为亮点。