当前位置: 首页 > news >正文

WebView 最佳封装模板(BaseWebActivity + WebViewHelper)

目标:封成一个 “可直接复用的组件”,不用每次都写一堆重复代码。

✅ 支持内开页面、不跳系统浏览器
✅ 支持进度条、标题、返回键处理
✅ 支持文件上传 / 拍照 / 相册
✅ 支持相机 / 麦克风权限
✅ 支持离线包 / 本地资源映射(HTTPS)
✅ 支持追加 Header(如 token)

WebViewHelper(统一初始化)

📌 负责 WebViewSettings / Cookie / 深色模式等

🔥 你只要在 Activity 中一行:WebViewHelper.init(webView)

// WebViewHelper.kt
package com.xxx.webimport android.os.Build
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeatureobject WebViewHelper {fun init(web: WebView) {val s = web.settingss.javaScriptEnabled = trues.domStorageEnabled = trues.databaseEnabled = trues.useWideViewPort = trues.loadWithOverviewMode = trues.cacheMode = WebSettings.LOAD_DEFAULTs.mediaPlaybackRequiresUserGesture = falseif (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:// 链接
  • 资源拦截(离线包 / 本地资源)
// SafeWebViewClient.kt
package com.xxx.webimport 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.WebViewAssetLoaderclass 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.urlval 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 专属:

// ChromeClient.kt
package com.xxx.webimport 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>>? = nulloverride 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 = callbackactivity.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 全搞定)

// BaseWebActivity.kt
package com.xxx.webimport android.os.Bundle
import android.view.View
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import com.xxx.yourapp.databinding.ActivityBaseWebBindingabstract class BaseWebActivity : AppCompatActivity() {private lateinit var binding: ActivityBaseWebBindingprivate lateinit var chrome: ChromeClientabstract fun provideStartUrl(): Stringopen 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,                    // 如有离线包:传 loaderonMainFrameError = { 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)

class MyWebActivity : BaseWebActivity() {override fun provideStartUrl() = "https://www.example.com"override fun provideWhiteHosts() = setOf("example.com","login.example.com",)
}

Done ✅

最终效果:

  • 可直接用于生产环境

  • 支持所有常见 WebView 功能

  • 已封装 最佳实践 + 安全策略 + 离线逻辑拓展点

下一篇:

Android WebView 最佳实践:Fragment 版本 + Token 注入 + 离线包热更新

http://www.dtcms.com/a/556818.html

相关文章:

  • 珲春市建设局网站中国设计网字体
  • 杭州英文网站建设杭州微信小程序外包
  • 顺序表vector--------练习题3题解
  • 触发器(Trigger):灵活控制窗口行为
  • mysql数据库自动备份_脚本_配置自动运行_windows下
  • Linux : 进程概念
  • 510-Spring AI Alibaba Graph Stream Node 示例
  • 【11408学习记录】考研英语长难句通关:2018真题精析,每日一句攻克阅读难点!​
  • 做网站买那种服务器龙港哪里有做阿里巴巴网站
  • TXT文件去重工具,一键快速去重复
  • ModelEngine vs Dify vs Coze:AI开发平台横评
  • 开封建站公司图片生成链接的网站
  • C++ STL:list|了解list|相关接口|相关操作
  • 【Java后端】配置属性BeanCreateException异常,使用默认值语法添加空串
  • 指针深入第二弹--字符指针、数组指针、函数指针、函数指针数组、转移表的理解加运用
  • 用红黑数封装实现map,set
  • PsTools 学习笔记(7.8):远程连接选项——连接性、超时、会话与安全基线
  • Java Se—异常
  • JSON.stringify() 方法详解
  • DevOps工具链对比,Azure 和 TikLab哪款更好用?
  • 安徽省住房城乡建设厅门户网站深圳百度快速排名优化
  • 一种无需IP核的FPGA RAM初始化方法:基于源码定义与赋值实现
  • openpnp - 坐标系统只有一个
  • 前端新技术解读:WebAssembly、Web Components 与 Tailwind CSS
  • 做a爱片网站做山西杂粮的网站
  • mysql保存二进制数据
  • 目标跟踪 deepsort
  • 网站建设前的分析第一小节内容好看网页设计
  • SAP PP生产版本批量维护功能分享
  • 【Linux】当遇到不是root用户,无法进入root用户,却仍需要使用sudo命令时