webkitx(Android WebView 最佳实践库)--> 上

功能清单

  • WebView 基线设置(JS/存储/深色模式/Cookie)

  • Fragment 容器:BaseWebFragment

  • Activity 容器:BaseWebActivity

  • 导航与资源拦截SafeWebViewClient

    • http/https 内开、非 http(s) 外跳

    • 仅 API 路由注入请求头(Authorization)

    • 离线包 路径映射(WebViewAssetLoader

  • UI/权限ChromeClientFragmentChromeClientActivity

    • 进度条、标题、<input type=file>、相机/麦克风权限钩子
  • 登录态TokenSync(推荐:Cookie 同步

  • 离线包热更新OfflineH5Manager(下载/校验/解压/切换/回滚)

  • 下载DownloadHelper + 系统 DownloadManager


1) 目录结构

复制代码

webkitx/ ├─ build.gradle ├─ proguard-rules.pro ├─ src/main/ │ ├─ AndroidManifest.xml │ ├─ java/com/yourorg/webkitx/ │ │ ├─ WebViewHelper.kt │ │ ├─ TokenSync.kt │ │ ├─ OfflineH5Manager.kt │ │ ├─ SafeWebViewClient.kt │ │ ├─ ChromeClientFragment.kt │ │ ├─ ChromeClientActivity.kt │ │ ├─ BaseWebFragment.kt │ │ ├─ BaseWebActivity.kt │ │ └─ WebKitx.kt // 可选:全局开关 │ ├─ res/layout/fragment_webkitx.xml │ ├─ res/layout/activity_base_webkitx.xml │ ├─ res/xml/network_security_config.xml │ └─ assets/error/net_error.html


2) Gradle & 依赖

settings.gradle

复制代码

include ':app', ':webkitx'

:webkitx/build.gradle

复制代码

plugins { id 'com.android.library'; id 'org.jetbrains.kotlin.android' } android { namespace 'com.yourorg.webkitx' compileSdk 34 defaultConfig { minSdk 21 consumerProguardFiles 'proguard-rules.pro' } buildFeatures { viewBinding true } } dependencies { implementation "androidx.appcompat:appcompat:1.7.0" implementation "androidx.core:core-ktx:1.13.1" implementation "androidx.activity:activity-ktx:1.9.2" implementation "androidx.fragment:fragment-ktx:1.8.3" implementation "androidx.webkit:webkit:1.11.0" }

:app 中加入 implementation project(':webkitx')


3) 必要资源/配置(一键复制)

AndroidManifest.xml

复制代码

<manifest> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <application android:networkSecurityConfig="@xml/network_security_config" /> </manifest>

res/xml/network_security_config.xml(如需 http 调试域)

复制代码

<network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">dev.yourdomain.com</domain> </domain-config> </network-security-config>

res/layout/fragment_webkitx.xml

复制代码

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/web_root" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web" android:layout_width="match_parent" android:layout_height="match_parent"/> <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="3dp" android:max="100" android:visibility="gone"/> <FrameLayout android:id="@+id/error_view" android:visibility="gone" android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>

res/layout/activity_base_webkitx.xml

复制代码

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web" android:layout_width="match_parent" android:layout_height="match_parent"/> <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="3dp" android:max="100" android:visibility="gone"/> </FrameLayout>

assets/error/net_error.html

复制代码

<!doctype html><meta charset="utf-8"><title>网络异常</title> <style>body{font-family:sans-serif;padding:24px}</style> <h2>网络开小差了</h2><p>请检查网络后重试</p>

proguard-rules.pro

复制代码

-keep class android.webkit.** { *; } -keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; } -dontwarn org.chromium.**


4) 关键类与职责(已实现)

  • WebViewHelper:初始化 WebSettings、深色模式、Cookie、调试

  • TokenSyncCookie 同步 (推荐),附带 token() 占位

  • OfflineH5Manager:离线包下载/校验/解压/切换/回滚;暴露 currentDir()hasBundle()

  • SafeWebViewClient:导航/拦截/SSL/错误;仅 API 路由注入请求头;离线包优先

  • ChromeClientFragment / ChromeClientActivity:进度/标题/文件选择(使用 ActivityResult)

  • DownloadHelper:系统 DownloadManager 下载 + 完成回调

  • BaseWebFragment:组合(Settings + Client + Chrome + Token + Offline)

  • BaseWebActivity:组合(同上)+ DownloadManager 集成

你只需要替换:白名单域名入口 URLTokenSync.token() 获取方式


5) 业务侧如何使用

A) Fragment 版(适合 ViewPager/Tab)

复制代码

class WebHostActivity : AppCompatActivity(R.layout.activity_host) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val frag = BaseWebFragment( startUrl = "https://yourdomain.com/index.html", whiteHosts = setOf("yourdomain.com","api.yourdomain.com","cdn.yourdomain.com"), domainsForCookie = listOf("yourdomain.com","api.yourdomain.com"), offline = OfflineH5Manager(this), enableDebug = BuildConfig.DEBUG ) supportFragmentManager.beginTransaction() .replace(R.id.container, frag) .commit() } override fun onBackPressed() { val f = supportFragmentManager.findFragmentById(R.id.container) as? BaseWebFragment if (f?.canGoBack() == true) f.goBack() else super.onBackPressed() } }

B) Activity 版(简单直接)

复制代码

class MyWebActivity : BaseWebActivity() { override fun startUrl() = "https://yourdomain.com/index.html" override fun whiteHosts() = setOf("yourdomain.com","api.yourdomain.com","cdn.yourdomain.com") override fun cookieDomains() = listOf("yourdomain.com","api.yourdomain.com") override fun offlineManager() = OfflineH5Manager(this) // 若启用离线包 }


6) 离线包更新(热更流程)

  1. 服务端提供 manifestversionzipUrlsha256

  2. App 下载 zip → offline.installFromZip(zip, version, sha256)

  3. 成功后 OfflineH5Manager.switchTo(version)(封装里已做)

  4. WebView 入口统一:

    • 在线:https://yourdomain.com/index.html

    • 离线:https://appassets.androidplatform.net/dynamic/index.html

  5. 如需回滚:offline.rollback()

BaseWebFragment/Activity 已根据 offline.hasBundle() 自动选择入口。


7) Token 注入策略(优先级)

  1. Cookie 同步(推荐)
    TokenSync.syncCookie(web, listOf("yourdomain.com","api.yourdomain.com"))

    后端从 Cookie 读取 AUTH_TOKEN

  2. 请求头注入(仅 API 路由)
    SafeWebViewClient.extraHeadersProvider = { mapOf("Authorization" to "Bearer ${TokenSync.token()}") }

    并通过 isApiPath(uri) 严格限制在 /api/**不要给静态资源加头

  3. JS 注入(备选)
    web.evaluateJavascript("window.__TOKEN__='${TokenSync.token()}';", null)


8) 下载(DownloadManager)

  • WebView 内部下载链接 → 交给系统 DownloadManager

  • 模块中已在 BaseWebActivity 里注册:

    • setDownloadListener{ url, ua, cd, mime, _ -> DownloadHelper.enqueue(...) }

    • 完成广播 DownloadHelper.registerCompletionReceiver(...)

Android Q+ 下载到公有 Downloads 目录无需手动存储权限;如需通知权限(Android 13+),在 App 动态申请 POST_NOTIFICATIONS


9) 扩展与开关

  • 域名白名单whiteHosts()

  • Cookie 同步域cookieDomains()

  • 仅 API 路由 :在 SafeWebViewClient.isApiPath 中自定义

  • 调试WebViewHelper.init(web, enableDebug = BuildConfig.DEBUG)

  • 错误页error_view 自定义 UI;或加载 assets/error/net_error.html


10) 上线安全核查清单

  • onReceivedSslError() 一律 cancel(禁止放行自签证书)

  • 仅对 API 路由 加请求头;禁止给静态资源加头

  • Scheme 外跳(weixin://, alipays://, tel:)留在白名单内显式处理

  • 仅在可信域 注入 @JavascriptInterface(如后续要加 Bridge)

  • 生产关闭不必要的 mixedContent;能 https 就 https


11) 最少需要你改的 3 处

  1. 域名yourdomain.comwhiteHosts & cookieDomains

  2. 入口 URLstartUrl

  3. Token 获取 :实现 TokenSync.token()

相关推荐
安卓兼职framework应用工程师8 小时前
android 15.0 app应用安装黑名单
android·pms·install·rom·安装黑名单
泷羽Sec-静安9 小时前
Less-7 GET-Dump into outfile-String
android·前端·网络·sql·安全·web安全
花花鱼9 小时前
html5与android之间相互调用
android
aqi0010 小时前
FFmpeg开发笔记(八十八)基于Compose的国产电视直播开源框架MyTV
android·ffmpeg·音视频·直播·流媒体
●VON11 小时前
双非大学生自学鸿蒙5.0零基础入门到项目实战 -《基础篇》
android·华为·harmonyos·鸿蒙
urkay-11 小时前
Android Cursor AI代码编辑器
android·人工智能·编辑器·iphone·androidx
pedestrian_h19 小时前
操作系统-线程
android·java·开发语言
gfdgd xi20 小时前
GXDE 内核管理器 1.0.1——修复bug、支持loong64
android·linux·运维·python·ubuntu·bug