目标:封成一个 "可直接复用的组件",不用每次都写一堆重复代码。
✅ 支持内开页面、不跳系统浏览器
✅ 支持进度条、标题、返回键处理
✅ 支持文件上传 / 拍照 / 相册
✅ 支持相机 / 麦克风权限
✅ 支持离线包 / 本地资源映射(HTTPS)
✅ 支持追加 Header(如 token)
WebViewHelper(统一初始化)
📌 负责 WebViewSettings / Cookie / 深色模式等
🔥 你只要在 Activity 中一行:
WebViewHelper.init(webView)
Kotlin
// WebViewHelper.kt
package com.xxx.web
import android.os.Build
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
object WebViewHelper {
fun init(web: WebView) {
val s = web.settings
s.javaScriptEnabled = true
s.domStorageEnabled = true
s.databaseEnabled = true
s.useWideViewPort = true
s.loadWithOverviewMode = true
s.cacheMode = WebSettings.LOAD_DEFAULT
s.mediaPlaybackRequiresUserGesture = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
s.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
}
// 深色模式跟随系统
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
WebSettingsCompat.setForceDark(s, WebSettingsCompat.FORCE_DARK_AUTO)
}
// Cookie 管理
val cm = CookieManager.getInstance()
cm.setAcceptCookie(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cm.setAcceptThirdPartyCookies(web, true)
}
}
}
SafeWebViewClient(跳转、离线包、错误处理)
📌 负责导航行为:内开 http/https,非 http(s) 外跳 APP
支持:
- URL 内开/外跳
- intent:// 链接
- 资源拦截(离线包 / 本地资源)
Kotlin
// SafeWebViewClient.kt
package com.xxx.web
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Message
import android.webkit.*
import androidx.webkit.WebViewAssetLoader
class SafeWebViewClient(
private val context: Context,
private val whiteHosts: Set<String> = emptySet(),
private val assetLoader: WebViewAssetLoader? = null,
private val onMainFrameError: (() -> Unit)? = null,
) : WebViewClient() {
override fun shouldOverrideUrlLoading(v: WebView, request: WebResourceRequest): Boolean {
val url = request.url
val scheme = url.scheme?.lowercase() ?: ""
// 非 http/https → 外跳系统或 App(如 weixin:// tel:)
if (scheme !in listOf("http", "https")) {
return try {
context.startActivity(Intent(Intent.ACTION_VIEW, url))
true
} catch (_: Exception) {
true
}
}
return false // ✅ WebView 内开
}
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
}
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
}
// 资源请求劫持(离线包、追加 header 等)
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
assetLoader?.let {
it.shouldInterceptRequest(request.url)?.let { return it }
}
return super.shouldInterceptRequest(view, request)
}
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
if (request.isForMainFrame) {
onMainFrameError?.invoke()
}
super.onReceivedError(view, request, error)
}
}
ChromeClient(文件选择、进度条、标题、全屏视频)
📌 UI 层功能 → WebChromeClient 专属:
Kotlin
// ChromeClient.kt
package com.xxx.web
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.webkit.*
class ChromeClient(
private val activity: Activity,
private val onProgressChanged: (Int) -> Unit = {},
private val onReceivedTitle: (String) -> Unit = {},
) : WebChromeClient() {
private var fileChooserCallback: ValueCallback<Array<Uri>>? = null
override fun onProgressChanged(view: WebView, newProgress: Int) {
onProgressChanged(newProgress)
}
override fun onReceivedTitle(view: WebView, title: String?) {
title?.let(onReceivedTitle)
}
// 处理 <input type="file">
override fun onShowFileChooser(
webView: WebView,
callback: ValueCallback<Array<Uri>>,
params: FileChooserParams
): Boolean {
fileChooserCallback?.onReceiveValue(null)
fileChooserCallback = callback
activity.startActivityForResult(params.createIntent(), FILE_CHOOSER_REQ)
return true
}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == FILE_CHOOSER_REQ) {
val result = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
fileChooserCallback?.onReceiveValue(result)
fileChooserCallback = null
}
}
companion object {
private const val FILE_CHOOSER_REQ = 2000
}
}
BaseWebActivity(一个 Activity 全搞定)
Kotlin
// BaseWebActivity.kt
package com.xxx.web
import android.os.Bundle
import android.view.View
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import com.xxx.yourapp.databinding.ActivityBaseWebBinding
abstract class BaseWebActivity : AppCompatActivity() {
private lateinit var binding: ActivityBaseWebBinding
private lateinit var chrome: ChromeClient
abstract fun provideStartUrl(): String
open fun provideWhiteHosts() = setOf<String>() // 支持白名单
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityBaseWebBinding.inflate(layoutInflater)
setContentView(binding.root)
val web = binding.webView
// 1) WebView 设置
WebViewHelper.init(web)
// 2) Navigation & 资源拦截(可支持离线包)
web.webViewClient = SafeWebViewClient(
context = this,
whiteHosts = provideWhiteHosts(),
assetLoader = null, // 如有离线包:传 loader
onMainFrameError = { showError() }
)
// 3) UI 层
chrome = ChromeClient(
activity = this,
onProgressChanged = { binding.progress.progress = it },
onReceivedTitle = { supportActionBar?.title = it }
)
web.webChromeClient = chrome
// ⏯ 启动加载
web.loadUrl(provideStartUrl())
}
private fun showError() {
binding.errorView.visibility = View.VISIBLE
}
override fun onBackPressed() {
if (binding.webView.canGoBack()) binding.webView.goBack()
else super.onBackPressed()
}
override fun onDestroy() {
binding.webView.apply {
stopLoading()
removeAllViews()
destroy()
}
super.onDestroy()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
chrome.onActivityResult(requestCode, resultCode, data)
super.onActivityResult(requestCode, resultCode, data)
}
}
使用方法(只要继承 BaseWebActivity)
Kotlin
class MyWebActivity : BaseWebActivity() {
override fun provideStartUrl() = "https://www.example.com"
override fun provideWhiteHosts() = setOf(
"example.com",
"login.example.com",
)
}
Done ✅
最终效果:
-
可直接用于生产环境
-
支持所有常见 WebView 功能
-
已封装 最佳实践 + 安全策略 + 离线逻辑拓展点