news 2026/9/17 9:22:39

web-to-app Hosts 广告拦截深度解析:过滤列表订阅、美化过滤与 APK 内编译规则

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
web-to-app Hosts 广告拦截深度解析:过滤列表订阅、美化过滤与 APK 内编译规则

web-to-app Hosts 广告拦截深度解析:过滤列表订阅、美化过滤与 APK 内编译规则

【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app

本篇围绕 web-to-app 的 Hosts Ad Blocking 功能展开:它会讲清这个基于 hosts 规则 + 美化(cosmetic)过滤的拦截器如何管理 20 个社区过滤列表、如何解析与匹配 ABP/hosts 规则、如何按页面注入隐藏 CSS 与反反拦截脚本,以及同一套编译规则如何同时服务应用内预览与导出 APK。读完之后,你既能掌握界面层面的订阅管理操作,也能从源码级别理解规则从下载、解析、索引到注入的完整链路。

功能定位与入口

Hosts Ad Blocking 是一个独立的过滤规则管理中心,入口在应用主界面的⋮ → Hosts Ad Blocking(对应文档 主界面 More)。它的职责有两层:

  • 规则库管理:下载、导入、启用/禁用、删除过滤列表(预设 20 个 + 用户自定义源),并提供规则搜索与一键清空。
  • 拦截引擎供给:宿主级阻断(hosts 规则)、网络级过滤(ABP 语法)、元素级隐藏(cosmetic 过滤),分别服务于应用内 WebView 预览和导出 APK 的运行期。

界面实现在 HostsAdBlockScreen.kt,引擎核心是单例 AdBlocker.kt(约 2800 行),磁盘缓存由 AdBlockFilterCache.kt 承担。

界面功能全览

与界面逐项对应,各功能在源码中的落点如下:

界面功能说明源码依据
活跃规则数顶部展示当前加载的阻断规则总量(getHostsFileRuleCount/getImportedHostsRuleCountHostsAdBlockScreen.kt#L119-L127
Sources 管理预设列表逐个下载;支持从文件导入(importHostsFromFile,系统文件选择器)、从 URL 导入(importHostsFromUrl)、删除源同屏文件内filePickerLauncher/importSource回调
自定义源卡片文件导入 / 自定义 URL 的源会获得显示名(优先取 ContentResolver 文件名或列表头! Title:),渲染为独立卡片,支持启用/禁用/删除getCustomHostsSources()(AdBlocker.kt#L1507-L1519)
内置列表20 个社区过滤列表(见下节)getPopularHostsSources()(AdBlocker.kt#L219-L324)
Search按名称/描述/URL 过滤预设与自定义源;另有 All / Downloaded / Not downloaded 三档过滤器HostsFilter枚举与filteredSources(HostsAdBlockScreen.kt#L86-L190)
Clear清空全部已加载规则,带确认对话框(WtaAlertDialog屏内showClearDialog状态

下载过程不是“点了就等”:importHostsFromUrl以 8KB 缓冲分块读取,进度回调被节流到每秒最多 6 次并始终发出最后一次,界面用LinearProgressIndicator展示字节数、速度与 ETA(AdBlocker.kt#L2200-L2302、DownloadProgress)。

内置列表源:20 个社区过滤列表

getPopularHostsSources()硬编码了 20 个预设源,覆盖 EasyList、uBlock Origin、AdGuard、AdAway 四大体系及 8 个语言/地区专属列表:

名称说明
EasyList国际主广告过滤列表(AdBlock 社区维护)
EasyPrivacy追踪器与分析阻断
uBlock FiltersuBlock Origin 默认过滤,面向现代广告技术
uBlock PrivacyuBlock Origin 隐私专用过滤
AdGuard Base广覆盖的国际广告过滤
AdGuard Tracking Protection分析与遥测阻断
AdGuard AnnoyancesCookie 提示、订阅弹窗等打扰
StevenBlack Hosts经典 hosts 文件源
AdAway DefaultAdAway 默认 hosts 列表
Peter Lowe's List广告/追踪服务器黑名单
1Hosts Lite轻量 hosts 列表
EasyList Germany德/奥/瑞域名
Liste FR (France)法国域名
EasyList Dutch荷/比域名
EasyList Italy意大利域名
EasyList Spanish西语/拉美域名
EasyList Portuguese葡/巴域名
AdGuard Japanese日语站点与日本广告网络
EasyList China中文站点
AdGuard DNS FilterDNS 层面的过滤规则

每个源携带name/url/description三元组(HostsSource),部分描述通过 Strings.kt 做多语言化——getPopularHostsSources()因此可动态取文案,而旧的POPULAR_HOSTS_SOURCES常量已被标记@Deprecated

规则导入:hosts 与 ABP 双格式自动识别

导入入口是parseFilterContent()(AdBlocker.kt#L2304-L2343)。它先扫描前 20 行判断内容属于 ABP 格式(出现[Adblock...]!||@@###@#等特征)还是传统 hosts 格式:

  • hosts 格式parseHostLine():兼容0.0.0.0 example.com127.0.0.1 example.com::1||example.com^以及裸域名行;随后经isValidHost()过滤 IP 地址、localhost系主机名、超长或含..的非法域名。
  • ABP 格式逐行交给parseAndAddRule(),按分隔符优先级(#@$##$##@?##?##@###)分派到四类解析器:
    • 网络规则(||host/path$script,third-party@@例外)→parseNetworkFilter()
    • 选择器隐藏(##.ad-banner##div[id^="taboola-"])→parseCosmeticFilter()
    • AdGuard CSS 注入(domain#$#selector { ... })→parseCssInjection()
    • 脚本规则(##+js(...)#%#//scriptlet(...))→parseScriptletRule(),注意它们必须优先于选择器解析处理,否则会把+js(...)负载当成非法 CSS 污染隐藏批次。

解析过程中还有一个工程细节:捕获OutOfMemoryError后触发 GC 并保留已解析的部分规则集(低内存设备上导入超大列表的保底策略),源码注释明确提到 AdGuard Base 单文件约 6.4MB。

ABP 模式到正则的翻译与惰性编译

translateAbpPattern()(AdBlocker.kt#L2572-L2608)完成 ABP 通配到 Java 正则源串的翻译:^先转为字符类[^\\w%.\\-](必须先于锚点转换,否则会破坏||host/path规则中插入的[^/]类),再处理||(子域前缀匹配)、|首/尾锚点、*通配,/regex/原样保留。MAX_ABP_PATTERN_LEN = 1024上限与连续星号检测用于防御恶意/畸形规则。

更关键的是两段式编译:导入时只做便宜的字符串翻译,昂贵的Regex编译延迟到规则首次命中时执行(NetworkFilter.regex())。源码注释给出了理由:一个 10 万条规则的列表若在导入期全部编译会驻留约 100MB 的 Pattern 对象,足以让 256MB 堆的设备 OOM——而导出 APK 时还会构建第二台引擎,压力加倍。匹配结果另有 2048 条上限的 LRU 缓存(blockResultCache),缓存键包含 URL、页面主机、资源类型与三态标记,保证例外规则(@@...$domain=...)跨页面结果正确。

匹配判定链路

shouldBlock()(AdBlocker.kt#L872-L916)的判定顺序:

  1. 命中SAFELIST_HOSTS(翻译、CDN、登录、支付、reCAPTCHA 等关键域白名单)直接放行;
  2. DOCUMENT 与 FONT 永不拦截,first-party 样式表也不拦截;
  3. 命中ESSENTIAL_RESOURCE_REGEX(jquery/react/api/login/checkout 等路径特征)放行;
  4. 主机精确命中exactHosts/hostsFileHosts后,再查例外规则决定是否放行;
  5. 依次查网络例外 → 网络阻断(带 anchor 域倒排索引加速,anchorDomainIndex+ 未锚定剩余索引);
  6. 兜底启发式:first-party 请求若路径命中FIRST_PARTY_AD_PATH_REGEX/ads//doubleclick//ad-slot/等),按 script/xhr/subdocument 类型阻断。

资源类型可由调用方传入,也可从请求头推断(inferResourceTypeFromRequest()依据Accept头与扩展名),这使拦截器既能服务shouldBlock(url, pageHost, resourceType, isThirdParty)也能直接消费WebResourceRequest

美化过滤(Cosmetic Filtering)

除 hosts 级阻断外,引擎内置一套 MutationObserver 驱动的元素隐藏机制(buildCosmeticRules())。每个页面的隐藏规则被按50 个选择器一批切分生成 CSS(display:none等六属性全!important),批次以getCosmeticHideBatches()暴露给页面内 DOM 观察脚本按批查询——这样单个非法选择器只会让它所在批次失效,不会连坐整条逗号拼接链(对应源码注释中的 issue #823 防护)。

CosmeticFilter数据结构(AdBlocker.kt#L86-L102)支持四种形态:

  • 普通选择器##.ad-banner###ad_top等,引擎还内置一批兜底选择器(ins.adsbygoogle[id^="div-gpt-ad"][class*="taboola"]等);
  • :style(...)重载(uBO 语法):只重排不隐藏,作为独立 CSS 规则输出,避免混入隐藏批次把“只想改颜色”的元素藏掉;
  • #$#完整 CSS 注入(AdGuard 语法):作为整条规则直接拼接;
  • 过程式伪类:has-text()/:upward()/:remove():编译为t:textu:argr操作码序列,由页面侧 JS 逐条求值(getCosmeticProceduralRulesJs());嵌套伪类或无法识别的 uBO 专属伪类(:xpath:watch-attr等 21 种)在解析期直接丢弃,防止非法 CSS 污染批次。

例外规则(#@#)按页面主机域匹配后从隐藏集合中按选择器精确抵消。所有 per-host 产物(CSS、反反拦截脚本、隐藏批次、过程式 JS)经cosmeticCache(128 条上限 + 版本号失效)按主机记忆化,规则变更统一走invalidateCache()/bumpCosmeticVersion()使缓存失效。

反反拦截(Anti-Adblock)脚本

buildAntiAdblockScript()(AdBlocker.kt#L1139-L1166)把页面命中的 scriptlet 与一段通用反检测脚本打包注入,通用部分(UNIVERSAL_ANTI_ADBLOCK_SCRIPT,AdBlocker.kt#L2627 起)覆盖五种常见检测手法:创建隐形诱饵广告元素、置位window.fuckAdBlock/google_ad_status等检测变量、用 MutationObserver 拆除 fixed/sticky 拦截墙并恢复被锁的滚动、代理getComputedStyle让诱饵元素“看起来可见”、拦截指向检测信标 URL 的 fetch/XHR 并伪造 200 响应。

scriptlet 支持abort-on-property-read/writeabort-current-inline-scriptset-constantremove-attrremove-classnano-setInterval/Timeout-boosterprevent-addEventListenerprevent-fetchprevent-xhr等十余种,每种都内联生成独立 JS(generateScriptlet())。

订阅生命周期:注册表、缓存与自动刷新

过滤源的状态落盘在filesDir下的两份文件:

  • adblock_hosts_sources.txt(registry v3):每行URL \t 规则数 \t 启用标志 \t 显示名 \t 上次刷新时间戳 \t 刷新间隔(saveSourcesRegistry()),v2/v3 新增列均为可选,向前兼容;
  • adblock_hosts.txt与编译态缓存(AdBlockFilterCache.saveCompiledState):重启后loadHostsRules()优先从编译态恢复,避免重新解析全部列表(AdBlocker.kt#L2411-L2433);仅配置 UI 需要的轻量元数据路径则走hydrateSourcesMetadata(),不加载完整过滤库,防止多张大列表耗尽堆内存。

自动刷新策略(AdBlocker.kt#L128-L145):

  • 刷新间隔取自列表头的! Expires: N days|hours,并被夹在 6 小时至 14 天之间,防止恶意或拼错的头造成热循环;无头则默认 24 小时;
  • kickSourceRefresh()在后台 IO 协程中刷新所有到期源(本应用订阅 + Hosts Blocking 已启用源的并集),失败源进入 1 小时内存退避,失败时保留旧内容(fail-soft);
  • 内容变化后在引擎互斥锁内rebuildEngine(),重建“与上次prepareRuntimeFilters完全相同的规则宇宙”,因此刷新不会与并发准备竞争;
  • 下载参数:15s 连接超时、30s 读取超时、User-Agent: WebToApp/1.0,内容同时写入 24 小时 URL 缓存(AdBlockFilterCache)。

预览与导出:同一规则集的两种消费方式

文档 Privacy & Hardening 将广告拦截列为主题能力之一,按应用的开关与订阅在 Ad Blocking 配置卡(Edit Common Config → Privacy)中选定——选择器同时提供内置列表与用户导入的自定义源。

预览侧WebViewActivity在页面导航前调用prepareRuntimeFilters(enabled, customRules, subscriptionUrls)(WebViewActivity.kt#L1504-L1561)装载规则;WebViewManager.kt 在请求拦截钩子中对每个子请求执行adBlocker.shouldBlock(url, pageHost, resType, isThirdParty)(L2012),命中后按资源类型返回伪造响应(脚本返回/* blocked */、图片返回 1x1 透明 GIF、XHR 返回{});页面注入阶段则拼接getCosmeticFilterCss(pageHost)(L2361)与getAntiAdblockScript(pageHost)(L5278-L5302)。

导出侧:构建 APK 时,config.adBlock.enabled为真则新建一台独立的AdBlocker实例,按该应用的订阅列表执行compileRulesText()(ApkBuilder.kt#L1576-L1594),把全部规则编译回 ABP 文本(||host^+ 原始 raw 规则行)后压缩写入assets/wta_adblock_compiled.txt,随包分发给导出应用的运行期引擎。这也解释了惰性编译注释中“导出会构建第二台引擎”的压力来源。

测试覆盖

该功能的单元测试集中在 app/src/test/java/com/webtoapp/core/adblock/:

  • AdBlockerTest.kt — 规则解析与匹配主逻辑;
  • AdBlockLazyRegexTest.kt — 验证正则延迟编译行为;
  • AdBlockSubscriptionRefreshTest.kt — 订阅刷新间隔、退避与重建;
  • AdBlockerCustomSourcesTest.kt — 自定义源显示名与卡片元数据;
  • AdBlockerHostRuntimeTest.kt — 运行期主机规则装载。

小结

Hosts Ad Blocking 的设计可以用一句话概括:界面是订阅管理器,AdBlocker是规则编译器,WebView 注入与 APK 导出只是规则集的两个出口。它通过 hosts/ABP 双格式自动识别、惰性正则编译、anchor 域倒排索引与 LRU 结果缓存把百万级请求的判定成本压到可接受区间;又用注册表版本化、! Expires:夹取、失败退避保证订阅在低内存 Android 设备上的长期可用性。若你为自己的 web 应用开启广告拦截,只需在 Privacy → Ad Blocking 中勾选所需订阅,预览即时生效,导出时规则即被编译进 APK,无需额外配置。

【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 9:21:28

PHP开发者转型指南:应对技术变革与职业焦虑

1. 现象观察:PHP程序员失业潮与失眠问题的关联性最近半年,我在技术社区和线下聚会中频繁遇到一个特殊现象:大量有5-10年经验的PHP开发者陷入长期失眠状态。这些开发者普遍具有相似特征——年龄集中在30-45岁之间,经历过互联网黄金…

作者头像 李华
网站建设 2026/9/17 9:21:23

Linux 动态库找不到:LD_LIBRARY_PATH 与 rpath

1. 从一次"库找不到"的报错说起error while loading shared libraries: libxxx.so.1: cannot open shared object file: No such file or directory这句报错,只要你写过程序、署过服务、折腾过编译,就一定见过。它最烦人的地方在于&#xff1a…

作者头像 李华
网站建设 2026/9/17 9:20:41

FilePizza 上手实践:不走服务器存储的浏览器 P2P 文件直传

FilePizza 上手实践:不走服务器存储的浏览器 P2P 文件直传 【免费下载链接】filepizza :pizza: Peer-to-peer file transfers in your browser 项目地址: https://gitcode.com/GitHub_Trending/fi/filepizza FilePizza 是一个开源的浏览器 P2P 文件传输工具&…

作者头像 李华
网站建设 2026/9/17 9:20:16

Spring Cloud Alibaba构建高并发优惠券查询系统实践

1. 项目背景与挑战作为"微赚淘客系统3.0"的核心研发成员,我们面临一个极具挑战性的技术需求:为微信公众号"查券助手"构建一个能够支撑日均200万次商品券查询请求、峰值QPS超过3500的高可用服务系统。这个系统需要满足以下几个核心需…

作者头像 李华
网站建设 2026/9/17 9:19:12

开放式代码评审实践:从流程设计到工具链落地的完整指南

做代码评审这行当久了,你会发现一个有意思的现象:很多人提起 code review 又爱又恨。爱的是它确实能挡掉不少低级 bug,恨的是它太依赖人的状态和自觉。项目一忙起来,评审就变成了走过场,绿点一点、 approve 一按&#…

作者头像 李华
网站建设 2026/9/17 9:18:53

YOLOv8模型改进实战:Backbone/Neck/Head优化与剪枝避坑指南

1. 这不是“YOLOv11”,但你必须先搞懂它才能真正改进模型先说一句大实话:目前官方并没有发布YOLOv11。Ultralytics官网最新稳定版本仍是YOLOv8,v9和v10均未正式开源(截至2024年中),所谓“YOLOv11”在主流学…

作者头像 李华