跳到主要内容

WebView 优化实战

场景

App 有大量 H5 页面通过 WebView 加载,首次打开白屏 2 秒以上。

排查与方案

1. WebView 加载耗时分析

阶段典型耗时优化方向
WebView 初始化200-500ms预创建
网络请求300-1000ms预加载 / 离线包
JS 解析执行200-500ms预执行 / 瘦身
渲染100-300ms骨架屏

2. 优化方案

// 1. WebView 预创建池:提前初始化,使用时直接取
object WebViewPool {
private val pool = LinkedList<WebView>()
private const val MAX_SIZE = 2

fun preload(context: Context) {
val appContext = context.applicationContext
repeat(MAX_SIZE) {
pool.add(WebView(MutableContextWrapper(appContext)))
}
}

fun obtain(context: Context): WebView {
val webView = if (pool.isNotEmpty()) pool.removeFirst() else WebView(context)
// 替换 Context 为当前 Activity
(webView.context as? MutableContextWrapper)?.baseContext = context
return webView
}

fun recycle(webView: WebView) {
webView.loadUrl("about:blank") // 清空页面
webView.clearHistory()
if (pool.size < MAX_SIZE) pool.add(webView)
}
}
// 2. 离线包方案:将 H5 资源打包到本地,拦截请求
class OfflineInterceptor : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView, request: WebResourceRequest
): WebResourceResponse? {
val url = request.url.toString()
// 匹配到离线资源则直接返回本地文件
val localFile = OfflinePackageManager.match(url)
if (localFile != null) {
return WebResourceResponse(
getMimeType(url),
"UTF-8",
localFile.inputStream()
)
}
return super.shouldInterceptRequest(view, request)
}
}

3. JSBridge 通信

// Native 注册方法供 JS 调用
webView.addJavascriptInterface(object {
@JavascriptInterface
fun getUserInfo(): String {
return Gson().toJson(UserManager.getUser())
}
}, "NativeBridge")

// JS 侧调用
// window.NativeBridge.getUserInfo()
安全

@JavascriptInterface 注解的方法暴露给 JS 调用,务必校验输入,不暴露敏感接口。Android 4.2 以下未加注解也能被调用,有注入风险。


面试答题要点

  1. WebView 预创建是最有效的优化(省掉初始化 200-500ms)
  2. 离线包拦截网络请求,消除网络耗时
  3. JSBridge 的实现方式和安全注意事项
  4. WebView 内存泄漏的处理(独立进程 / 手动 destroy)

相关链接