☰
Android-Touch-Helper(开屏跳过):基于无障碍服务的安卓开屏广告自动跳过助手技术全解析
2026/10/13 6:29:25 网站建设 项目流程
  • 移动开发
  • GUI 自动化

【免费下载链接】Android-Touch-Helper

AdSkip — an Android assistant for automatically skipping app launch ads

项目地址:https://gitcode.com/gh_mirrors/an/Android-Touch-Helper
点击查看免费下载

本文系统解析开源项目 Android-Touch-Helper(中文名"开屏跳过")的技术实现与使用配置。该项目基于 Android 无障碍服务(AccessibilityService)实现对应用开屏广告的自动检测与跳过,提供关键字、指定控件、指定位置三种跳过策略,并在隐私、性能与可用性上做了细致的工程处理。读完本文,你将掌握该工具的工作原理、全部配置项含义、三种跳过方式的自定义流程,以及其源码级的事件处理、遍历调度与点击升级机制。

一、项目概览:什么是"开屏跳过"

"开屏跳过"(Android-Touch-Helper)是一款安卓系统的开屏广告自动跳过助手。它基于 Android 的Accessibility(无障碍服务)实现:开启了无障碍服务的软件可以获取屏幕上的任何内容,从而检测广告界面并帮助用户自动点击"跳过"按钮(见 README.zh-CN.md)。

与多数同类工具一样,无障碍类工具理论上能够接触到屏幕上的敏感信息,因此隐私是这类应用最大的顾虑。本项目的明确承诺是:

本程序是开源程序,不需要网络权限,不需要存储权限,不会收集或者上传任何信息!绝无隐私问题!

这一承诺不仅写在文档里,也直接体现在工程结构上:AndroidManifest.xml 中没有任何<uses-permission>声明,应用的<queries>仅声明了查询可启动应用(LAUNCHER)、桌面程序(HOME)与输入法(InputMethod)的 intent,用于构建工作集与白名单,不涉及网络与存储访问。

程序可以使用三种方法跳过开屏广告:

  1. 关键字:在应用中查找含有关键字的按钮,发现后自动点击以跳过广告;
  2. 指定控件:为应用预先配置目标控件,应用启动时自动查找并点击;
  3. 指定位置:为应用预先配置屏幕坐标区域,应用启动时自动点击该区域。

三种方法可以在"设置"中具体配置,对应代码中的skipAdByKeyword、skipAdByActivityWidget、skipAdByActivityPosition三个开关(见 TouchHelperServiceImpl.java)。

二、隐私与安全设计:无网络、无存储、可审计

2.1 权限面最小化

  • 无网络权限:AndroidManifest.xml未申请INTERNET,程序无法联网收发数据;
  • 无存储权限:未申请读写外部存储的任何权限,规则数据只保存在应用私有SharedPreferences中(见 Settings.java,文件名为TouchHelper_Config);
  • 开源可审计:全部源码位于仓库app/src/main/java下,任何开发者都可以审查其行为。

2.2 屏幕内容的"披露确认"机制

由于无障碍服务会读取当前屏幕上的界面内容(控件文字、描述与位置),项目加入了强制性的应用内披露确认:

  • 未在应用内点击"同意"前,服务不读取任何界面内容:onAccessibilityEvent()第一步即检查Settings.isDisclosureAccepted(),未同意则直接返回并提示用户(见 TouchHelperServiceImpl.java);
  • 确认状态持久化到SharedPreferences的ACCESSIBILITY_DISCLOSURE_ACCEPTED键,首次使用默认false(见 Settings.java);
  • 披露文案明确说明:自动识别时屏幕内容只在本机内存中即时比对、不保存;仅当用户主动使用"添加程序的跳过方法"时,所选控件的文字、描述和坐标才会作为规则保存在本机应用数据中,可随时在设置中查看或删除;"复制窗口控件"功能则是按用户操作写入系统剪贴板(见 strings.xml 的accessibility_disclosure_message)。

披露弹窗由 HomeFragment.java 的showAccessibilityDisclosure()触发:用户点击"无障碍"入口按钮时弹出;若服务已开启但披露未确认,回到应用主页时也会再次提示。

三、工作原理:无障碍服务如何"看见"并"点击"

3.1 服务声明与配置

核心服务 TouchHelperService.java 继承自AccessibilityService,其行为由 accessibility_service_config.xml 声明:

  • accessibilityEventTypes:仅监听typeWindowContentChanged与typeWindowStateChanged两类事件,范围克制;
  • canRetrieveWindowContent="true":允许获取窗口内容(遍历节点树);
  • canPerformGestures="true":允许派发触摸手势(用于模拟点击);
  • accessibilityFlags:包含flagRetrieveInteractiveWindows、flagReportViewIds、flagIncludeNotImportantViews,以拿到足够完整的节点信息;
  • notificationTimeout="50":事件合并窗口为 50ms。

服务在onServiceConnected()时初始化:读取设置、加载关键字与自定义规则、通过PackageManager枚举已安装应用构建"工作集"、注册广播接收器、创建单线程调度执行器(ScheduledExecutorService),并在onUnbind()时逐一注销与关闭(见 TouchHelperServiceImpl.java)。

3.2 事件驱动:什么时候开始"跳广告流程"

onAccessibilityEvent()是核心入口(见 TouchHelperServiceImpl.java):

  • TYPE_WINDOW_STATE_CHANGED:窗口状态变化(打开 Activity、弹出 Dialog 等)。代码先判断当前包名与 Activity 名:
    • 切换到新包且是 Activity 时,结束旧的跳广告流程;若该包在工作集中,则启动新的跳广告流程(startSkipAdProcess()),这是启动流程的唯一入口;
    • 同一包内 Activity 变化时保持流程运行并扫描新窗口(应对"先主界面后广告页"的常见广告形态);
    • 若锁屏/灭屏时系统窗口盖住了目标应用,则记录wakeupPending,等解锁后该应用窗口回归时按"温启动"处理并重启流程。
  • TYPE_WINDOW_CONTENT_CHANGED:内容变化,是频率最高的事件。代码做了快速路径优化:当前没有跳广告流程在运行时,直接返回,避免为无关事件付出任何遍历代价。

3.3 工作集(Working List)的构建

updatePackage()维护了一个"哪些应用需要被跳过广告"的集合:

  • 枚举所有带CATEGORY_LAUNCHER的可启动应用作为候选;
  • 再找出所有桌面(CATEGORY_HOME)、输入法应用(通过InputMethodManager),连同本应用与com.android.settings一起排除;
  • 最后移除用户在白名单中勾选的应用,得到最终工作集;
  • 该集合在应用安装/卸载广播(ACTION_PACKAGE_ADDED/REMOVED)或白名单变更时自动重建。

3.4 一次"跳广告流程"的生命周期

startSkipAdProcess()会:

  1. 新建一个ClickSession(每轮流程的点击状态表,见下文"点击升级");
  2. 置位三种跳过方式开关(位置 / 控件 / 关键字);
  3. 清空待扫描队列;
  4. 通过ACTION_STOP_SKIPAD消息在skipAdDuration秒(默认 4 秒)后自动结束本轮流程——时间过短广告可能尚未加载完成,过长则可能误点进入应用后的正常界面,这就是设置中"检测时长"的意义。

3.5 模拟点击的实现

点击统一由click(x, y, start_time, duration)完成:构造GestureDescription路径并调用service.dispatchGesture()派发真实触摸手势(见 TouchHelperServiceImpl.java)。此外还会先尝试节点自身的ACTION_CLICK语义动作,失败或失效时再升级为手势(详见第七章)。

四、三种跳过方式的配置与源码实现

4.1 方式一:关键字匹配

这是开箱即用的默认方式,缺省关键字为"跳过"(Settings中KEY_WORDS_LIST的默认值为["跳过"],见 Settings.java)。

关键字规则在 SkipAdRules.java 中实现,属于纯 Java 逻辑(刻意不依赖 Android 框架),以便在 JVM 上做单元测试:

  • parseKeywords():用户输入的关键字串按空白分隔、去除空串与重复项;空关键字会被丢弃,否则会误匹配屏幕上几乎所有短文本;
  • findKeyword():按优先级顺序遍历关键字,同时匹配节点text与contentDescription;
  • 匹配条件:label.length() <= keyword.length() + KEYWORD_MAX_EXTRA_LENGTH (6)且label.contains(keyword)——即"跳过 3""跳过广告 5"这类带倒计时/后缀的按钮也能命中,但过长标签不会误匹配;
  • 特殊排除:程序自身应用名"开屏跳过"(SelfPackageName)绝不能被当作跳过目标点击(防止误点本应用)。

上述规则均有对应单元测试验证,见 SkipAdRulesTest.java(如findKeyword_rejectsLabelsTooMuchLongerThanKeyword、findKeyword_ignoresOwnAppName等)。

配置路径:设置 → 跳过广告按钮的关键字(setting_key_words),多个关键字以空格分隔。修改后通过ACTION_REFRESH_KEYWORDS消息即时同步给无障碍服务线程。

4.2 方式二:指定控件匹配

当关键字无法覆盖某些广告时,可以为具体应用添加"指定控件"规则。规则模型为 PackageWidgetDescription.java,字段包括:

字段含义
packageName/activityName规则适用的应用包名与 Activity 名
className/idName控件的类名与 viewId 资源名
description/text控件的内容描述与文本
position控件在屏幕上的边界矩形
clickable控件是否可点击(记录于采集时)
onlyClick是否跳过ACTION_CLICK直接使用手势点击

匹配逻辑findWidget()(见 SkipAdRules.java)按顺序尝试:

  1. 精确屏幕边界相等;
  2. viewId精确相等;
  3. contentDescription包含关系;
  4. text包含关系。

所有字段均做空值保护(规则 JSON 导入可能缺字段)。测试用例findWidget_matchesExactBounds、findWidget_matchesViewId、findWidget_matchesDescriptionAndTextByContains覆盖了这四种匹配路径。

采集与添加:设置 → 高级设置 → "添加程序的跳过方法",会弹出可拖动的工具条(TYPE_ACCESSIBILITY_OVERLAY悬浮层)。流程为:完全关闭目标应用 → 重新启动 → 广告出现时点工具条"显示布局"(遍历并绘制所有节点边框)→ 点击目标控件使其高亮选中 → 点"添加控件"。控件数据以 JSON 形式持久化在Settings的PACKAGE_WIDGETS键中,详见 settings.md 与showActivityCustomizationDialog()实现。

管理与共享:"管理已添加按钮的程序"(MultiSelectListPreference)可勾选保留或删除某应用的控件规则;"导入/导出程序按钮跳过的规则"(ManagePackageWidgetsDialogFragment.java)支持将规则 JSON 复制到剪贴板/从剪贴板粘贴,实现规则的导出分享与批量导入(保存时通过 Gson 反序列化校验,格式错误会提示)。

4.3 方式三:指定位置匹配

对无文字、无控件 ID 的纯图形广告区域,可配置"指定位置"。规则模型为 PackagePositionDescription.java,字段为packageName、activityName与x、y坐标。

添加方式与控件法类似:工具条中"显示准心"出现可拖动的准心图标,拖到目标广告的跳过区域后"添加坐标"即可。规则保存在Settings的PACKAGE_POSITIONS键中。

执行逻辑见TouchHelperServiceImpl的位置分支:

  • 在目标应用的TYPE_WINDOW_STATE_CHANGED事件中触发一次;
  • 采用scheduleAtFixedRate定时重试:首次延迟 300ms(PACKAGE_POSITION_CLICK_FIRST_DELAY),之后每隔 500ms(PACKAGE_POSITION_CLICK_RETRY_INTERVAL)重试,最多 6 次(PACKAGE_POSITION_CLICK_RETRY);
  • 每次重试前校验当前 Activity 名与规则中的activityName一致,避免广告关闭后继续点击主界面;流程结束或取消时cancel(true)终止任务。

五、核心配置项详解

所有设置项定义于 touch_helper_preference.xml,与 Settings.java 中的持久化键一一对应:

配置项键名默认值 / 取值范围说明
跳过广告时显示通知skip_ad_notification开(true)每次命中目标执行点击时弹 Toast 提示"正在根据关键字/控件/位置跳过广告…",按 5 秒内最多一次的节奏抑制
检测时长skip_ad_duration4 秒,范围 1–10 秒目标应用启动后在此窗口内检测广告,之后不再检测;过短广告未加载完、过长易误点
跳过广告按钮的关键字setting_key_words"跳过"空格分隔多个关键字,按输入顺序决定匹配优先级
应用程序白名单setting_whitelist首次运行默认勾选全部系统应用勾选的应用不参与跳过检测;列表按拼音排序并优先显示已勾选项
添加程序的跳过方法setting_activity_customization—打开悬浮工具条,手动添加控件或坐标规则;服务未运行时提示先开启无障碍服务
管理已添加按钮/区域的程序setting_activity_widgets/setting_activity_positions—以勾选方式保留或删除某应用的规则
导入/导出程序按钮跳过的规则setting_activity_widgets_advanced—编辑控件规则 JSON,支持复制/粘贴、校验后保存
高级设置说明——跳转到项目网站的使用说明页

设置变更通过TouchHelperService.dispatchAction()发送消息(ACTION_REFRESH_KEYWORDS、ACTION_REFRESH_PACKAGE、ACTION_REFRESH_CUSTOMIZED_ACTIVITY等,见 TouchHelperService.java),由主线程 Handler 处理后无缝更新服务端的内存快照,无需重启服务。

六、保活设置:让后台服务不被系统杀死

无障碍服务是后台常驻程序,但安卓系统多样性会导致其被中止,从而无法跳过广告。项目文档 enable.md 总结了四类保活检查项,主页 HomeFragment.java 也会以"√/×"图标实时展示无障碍服务与电池优化两项状态:

  1. 在无障碍设置里开启本服务:系统设置 → 无障碍/辅助功能 → 打开"开屏跳过-无障碍服务";
  2. 电源优化里选择"不优化":标准安卓系统的ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS页面即可设置,但各 ROM 不一定完全遵守;
  3. 在特定 ROM 的电池管理中加入白名单(各品牌路径不同):
    • vivo:设置 → 电池 → 后台耗电管理 → 允许后台高耗电;
    • OPPO:设置 → 电池 → 应用耗电管理 → 开屏跳过 → 允许完全后台行为、允许自启动;
    • 小米:应用管理 → 开屏跳过 → 省电策略"无限制"、自启动"允许";
    • 华为:应用启动管理 → 关闭"自动管理" → 手动允许自启动与后台活动;
    • 三星:设置 → 应用程序 → 电池 → 允许后台活动,或 常规管理 → 电池 → 后台活动限制 → 自动运行应用程序;
  4. 在任务管理/后台清理里锁定本程序,避免被一键清理误杀。

七、源码级纵深:遍历调度、反压与点击升级

7.1 节点遍历:BFS 与规则优先级

iterateNodesToSkipAd()对窗口节点树做广度优先遍历(见 TouchHelperServiceImpl.java),并在同一次遍历中同时处理控件规则与关键字规则:

  • 控件规则命中即点击,优先级高于关键字:即使关键字节点先被遍历到,也只会被记录为候选,等整棵树没有控件规则命中时才回退点击关键字候选(对应测试widgetRule_keepsPriorityOverAnEarlierKeywordMatchInTheSameTree);
  • 遍历过程中不断检查skipAdRunning与当前ClickSession是否仍有效,防止流程结束后"迟到的点击"落入新一轮流程;
  • 每个AccessibilityNodeInfo使用后立即recycle(),测试traversal_recyclesEveryNodeItObtains专门验证无节点泄漏。

7.2 反压机制:广告界面高频率事件的应对

广告屏每秒可能发出数十个TYPE_WINDOW_CONTENT_CHANGED事件,而每次遍历都需要按节点进行 Binder 往返,若不加以控制,工作线程会堆积过期子树,最新内容反而最后被扫描。为此实现了一个小型待扫描队列:

  • 队列上限MAX_PENDING_TRAVERSALS = 3;
  • 同源去重:同一子树重复上报时,用最新快照替换旧快照(AccessibilityNodeInfo.equals()比较源 viewId 与窗口 id),保证同子树只扫描一次且看到的是最新状态;
  • 逐出 + 兜底重扫:队列满时逐出最旧的非关键扫描,并安排一次限速的全窗口重扫(RESCAN_MIN_INTERVAL_MS = 1000)弥补遗漏;
  • 窗口状态扫描不可逐出(sticky),保证全窗口扫描必然执行;
  • 遍历调度器每处理一个子树就重新入队,让定时位置点击等其他任务能在两次扫描之间获得执行机会。

对应测试包括backPressure_keepsOnlyTheNewestSubTreesWhenWorkerIsBusy、backPressure_neverEvictsTheFullWindowScan、backPressure_collapsesEventsForTheSameSourceAndKeepsTheNewestSnapshot等。

7.3 点击升级:从 ACTION_CLICK 到真实手势

部分广告 SDK 会"吞掉"ACTION_CLICK(返回成功但无实际效果)。项目的应对策略是(见clickNode()/clickByGesture()):

  1. 第一次命中:调用节点performAction(ACTION_CLICK);若失败,再尝试其父节点(文字标签本身常不可点,可点的是容器);父节点也失败则立即降级为手势;
  2. 同一控件再次出现在屏幕上(500ms 后,CLICK_RETRY_MIN_INTERVAL_MS):判定首次ACTION_CLICK可能是假成功,升级为在控件中心派发真实触摸手势;
  3. 每轮流程每个控件最多点击 2 次(MAX_CLICK_ATTEMPTS = 2),之后该控件在本轮流程中放弃;ClickSession每轮新建,用户重新进入应用后重新计数;
  4. 手势派发前的安全检查:刷新节点确认控件仍存在 → 确认节点仍属于工作集内的包 → 按原规则重新校验仍匹配 → 确认边界非空、中心点在屏幕范围内、isVisibleToUser()为真。任一不满足即放弃,拒绝的尝试不计入次数(测试click_neverDispatchesAGestureAtInvalidCoordinates、click_gestureIsNotDispatchedWhenTheWidgetIsGone、click_refusedGesturesDoNotConsumeAttempts等覆盖)。

7.4 锁屏唤醒与"温启动"广告

部分应用在锁屏解锁后(前台应用"温启动")也会展示开屏广告。项目通过双通道检测:

  • 广播通道:注册ACTION_USER_PRESENT与ACTION_SCREEN_ON两个受保护广播,收到后对当前前台目标应用重启跳广告流程,并立即扫描 + 1 秒后再扫一次(WAKEUP_RESCAN_DELAY_MS = 1000,因为广告往往在应用恢复后才渲染);
  • 事件推断通道:部分机型(如 OPPO)不向第三方应用投递USER_PRESENT,因此代码会在锁屏/灭屏系统窗口盖住目标应用时置位wakeupPending,待该应用窗口回归且设备已解锁时按温启动处理,完全不依赖广播。

isScreenLockedOrOff()综合KeyguardManager.isKeyguardLocked()与PowerManager.isInteractive()判断屏幕与锁屏状态。上述行为均有 Robolectric 测试验证(见 TouchHelperServiceImplTest.java,如warmStart_afterTheLockScreenRestartsTheProcessWithoutAnyBroadcast)。

八、测试保障体系

仓库包含两套测试,覆盖核心逻辑:

  • SkipAdRulesTest.java:纯 JVM 单元测试,验证关键字解析(空格分割、去重、空串过滤)、关键字匹配(优先级、长度上限、排除自身应用名)与控件规则匹配(精确边界、viewId、描述/文本包含、空字段容错);
  • TouchHelperServiceImplTest.java:基于 Robolectric 的集成式测试,用真实的AccessibilityEvent与AccessibilityNodeInfo树驱动服务,覆盖流程启停、关键字点击、控件优先级、反压去重与逐出、点击升级、锁屏温启动、披露门控(未同意不读屏)、节点回收与线程清理等场景。

九、项目状态与获取方式

项目作者在 README.zh-CN.md 中说明:这是一个数年前开始的个人项目,目前已进入低维护状态,新需求可能难以满足,但欢迎社区提交 PR(作者会尽量 review 后 merge)。项目官网为https://touchhelper.zfdang.com,应用曾上架 F-Droid 与 Google Play(包名com.zfdang.touchhelper),历史 release APK 亦存档于 docs/ 目录下。文档方面,除本 README 外,enable.md 提供保活设置教程,settings.md 提供三种跳过方式的详细操作指引。

适用前提与限制:本文所有行为描述均基于当前仓库源码(app/src/main、app/src/test)与文档;无障碍服务的可用性、事件可达性与各 ROM 后台策略因设备差异而不同,实际效果以目标设备为准。由于项目处于低维护状态,使用前建议结合上述源码与测试自行评估其行为是否满足需求。

  • 移动开发
  • GUI 自动化

【免费下载链接】Android-Touch-Helper

AdSkip — an Android assistant for automatically skipping app launch ads

项目地址:https://gitcode.com/gh_mirrors/an/Android-Touch-Helper
点击查看免费下载

相关推荐

上一篇:百度网盘直链解析工具深度解析:技术架构与高效下载实践
下一篇:百度网盘直链解析技术深度解析:架构剖析与性能优化策略

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询