Android利用Accessibility Service获取当前页面的组件信息

打印 上一主题 下一主题

主题 692|帖子 692|积分 2076

1、自界说AccessibilityService

继承 AccessibilityService,重写 onInterrupt() 和 onAccessibilityEvent() 方法(这两个方法是 必须重写 的)。在onAccessibilityEvent() 方法中,val rootNode = rootInActiveWindow 获取当前活动窗口的根节点。rootInActiveWindow 是 AccessibilityNodeInfo 类型的一个属性,它代表了当前活动窗口的根节点。这个根节点是一个包含所有 UI 组件的树形结构的顶层节点,可以从它开始递归地遍历整个界面元素。
  1. package com.codelab.basics
  2. import android.accessibilityservice.AccessibilityService
  3. import android.graphics.Rect
  4. import android.os.Environment
  5. import android.util.Log
  6. import android.view.accessibility.AccessibilityEvent
  7. import android.view.accessibility.AccessibilityNodeInfo
  8. import java.io.File
  9. class MyAccessibilityService : AccessibilityService() {
  10.     override fun onAccessibilityEvent(event: AccessibilityEvent?) {
  11.         val rootNode = rootInActiveWindow//用于获取当前活动窗口的根节点
  12.         if (rootNode != null) {
  13.             val xmlContent = StringBuilder()
  14.             xmlContent.append("<?xml version="1.0" encoding="UTF-8"?>\n")
  15.             xmlContent.append("<ViewHierarchy>\n")
  16.             buildXml(rootNode, xmlContent, 1)
  17.             xmlContent.append("</ViewHierarchy>")
  18.             //将生成的 XML 内容先打印到日志中
  19.             Log.d("GeneratedXML", xmlContent.toString())
  20.             // 保存到文件
  21.             saveToFile("view_hierarchy_full.xml", xmlContent.toString())
  22.         }
  23.     }
  24.     override fun onInterrupt() {
  25.         // 无障碍服务被中断时的回调
  26.     }
  27.     private fun buildXml(node: AccessibilityNodeInfo?, xmlContent: StringBuilder, depth: Int) {
  28.         if (node == null) return
  29.         // 添加缩进
  30.         val indent = "    ".repeat(depth)
  31.         // 开始标签
  32.         xmlContent.append("$indent<View ")
  33.         // 获取所有公开属性
  34.         val bounds = Rect()
  35.         node.getBoundsInScreen(bounds) // 获取节点在屏幕上的边界
  36.         xmlContent.append("class="${node.className}" ")
  37.         xmlContent.append("text="${node.text}" ")
  38.         xmlContent.append("description="${node.contentDescription}" ")
  39.         xmlContent.append("viewId="${node.viewIdResourceName}" ")
  40.         xmlContent.append("packageName="${node.packageName}" ")
  41.         xmlContent.append("bounds="${bounds.flattenToString()}" ")
  42.         xmlContent.append("clickable="${node.isClickable}" ")
  43.         xmlContent.append("enabled="${node.isEnabled}" ")
  44.         xmlContent.append("focusable="${node.isFocusable}" ")
  45.         xmlContent.append("focused="${node.isFocused}" ")
  46.         xmlContent.append("scrollable="${node.isScrollable}" ")
  47.         xmlContent.append("selected="${node.isSelected}" ")
  48.         xmlContent.append("checkable="${node.isCheckable}" ")
  49.         xmlContent.append("checked="${node.isChecked}" ")
  50.         // 如果有子节点,则保留内部层次
  51.         if (node.childCount > 0) {
  52.             xmlContent.append(">\n")
  53.             for (i in 0 until node.childCount) {
  54.                 buildXml(node.getChild(i), xmlContent, depth + 1)
  55.             }
  56.             xmlContent.append("$indent</View>\n")
  57.         } else {
  58.             // 无子节点直接闭合标签
  59.             xmlContent.append("/>\n")
  60.         }
  61.     }
  62.     private fun saveToFile(fileName: String, content: String) {
  63.         try {
  64.             val file = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName)
  65.             file.writeText(content)
  66.             Log.d("MyAccessibilityService", "XML 文件已保存: ${file.absolutePath}")
  67.         } catch (e: Exception) {
  68.             Log.e("MyAccessibilityService", "保存文件时出错: ${e.message}")
  69.         }
  70.     }
  71. }
复制代码
2、Service注册

AccessbilityService本质还是Service,所以需要在 AndroidManifest.xml 中进行注册:
  1. <service
  2.             android:name=".MyAccessibilityService"
  3.             android:exported="false"
  4.             android:label="BasicsCodelab"
  5.             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
  6.             <intent-filter>
  7.                 <action android:name="android.accessibilityservice.AccessibilityService" />
  8.             </intent-filter>
  9. </service>
复制代码


  • android:name=".MyAccessibilityService"

    • 该属性指定无障碍服务的类名称,MyAccessibilityService 是一个继承自 AccessibilityService 的类。这个类负责处置惩罚无障碍相关的变乱。

  • android:exported="false"

    • 用于明确标识该服务是否可以被其他应用启动或绑定。设置为 false 表现该服务不会被外部应用访问,只能由系统或该应用内访问。对于无障碍服务,通常设置为 false。

  • android:label="BasicsCodelab"

    • 该属性为无障碍服务指定了一个用户友好的名称。在设备的设置界面中,用户可以看到该服务的名称。BasicsCodelab 是你的服务显示的名称,可以根据需要更改为你的应用或服务的名称。

  • android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"

    • 这个权限声明白该服务需要一个特定的权限才气绑定到无障碍服务。BIND_ACCESSIBILITY_SERVICE 权限是系统权限,通常只能由系统或具有相应权限的应用绑定,确保只有合适的应用能够访问这个服务。

  • <intent-filter>

    • 在这个 intent-filter 中,<action android:name="android.accessibilityservice.AccessibilityService" /> 使系统知道该服务是一个无障碍服务。
    • 这告诉 Android 系统,该服务的 action 是 android.accessibilityservice.AccessibilityService,它是系统辨认无障碍服务的尺度动作。

  • <meta-data>

    • meta-data 元素用于指示系统该服务的配置文件位置。它的 android:name="android.accessibilityservice" 属性是系统所需的标识符。
    • android:resource="@xml/accessible_service_config" 指定了该服务的配置文件 accessible_service_config.xml,该文件位于 res/xml/ 目录中。
    • accessible_service_config.xml 是一个 XML 配置文件,其中界说了无障碍服务的具体配置,如服务接收的变乱类型、反馈类型、设置页面等。

3、监听相关配置

在 res 文件夹下xml文件夹 (没有的话新建),然后新建一个配置xml文件

在里面写入如下代码:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:accessibilityEventTypes="typeAllMask"
  4.     android:accessibilityFeedbackType="feedbackGeneric"
  5.     android:accessibilityFlags="flagDefault"
  6.     android:canRetrieveWindowContent="true"
  7.     android:notificationTimeout="100"
  8.     android:description="@string/app_name"
  9.     android:packageNames="com.codelab.basics"/>
复制代码


  • android:accessibilityEventTypes="typeAllMask"

    • 界说服务接收的无障碍变乱类型。
    • typeAllMask 表现接收所有类型的无障碍变乱。
    • 其他常用变乱类型:

      • typeViewClicked: 视图被点击变乱。
      • typeViewFocused: 视图得到焦点变乱。
      • typeWindowStateChanged: 窗口状态变化变乱。


  • android:accessibilityFeedbackType="feedbackGeneric"

    • 指定服务提供的反馈类型。
    • feedbackGeneric 是通用的反馈类型,实用于多种无障碍服务场景。
    • 其他反馈类型:

      • feedbackSpoken: 语音反馈。
      • feedbackHaptic: 触觉反馈(振动)。
      • feedbackAudible: 音频反馈。
      • feedbackVisual: 视觉反馈。


  • android:accessibilityFlags="flagDefault"

    • 配置无障碍服务的行为标志。
    • flagDefault 是默认标志,通常用于尺度无障碍服务。
    • 其他标志:

      • flagIncludeNotImportantViews: 包罗非紧张视图(例如装饰性视图)。
      • flagRequestTouchExplorationMode: 哀求触摸探索模式(用于支持触摸屏的服务)。


  • android:canRetrieveWindowContent="true"

    • 指定服务是否可以检索窗口内容。
    • 假如设置为 true,服务可以访问窗口的内容(例如视图层次结构)。
    • 对于需要操纵或查询视图节点的无障碍服务,这个属性必须设置为 true。

  • android:notificationTimeout="100"

    • 设置无障碍变乱的关照超时时间,单位为毫秒。
    • 用于合并频繁的变乱,避免服务处置惩罚过多的变乱关照。

  • android:description="@string/app_name"

    • 提供无障碍服务的形貌信息,通常显示在设备的无障碍设置页面中。
    • @string/app_name 指向 strings.xml 中界说的字符串资源。可以修改为更具体的形貌,例如 "This service provides additional accessibility features."。

  • android:packageNames="com.codelab.basics"

    • 指定服务监听的目的应用的包名。
    • 假如留空,则表现服务可以接收所有应用的无障碍变乱。
    • 当仅需要监听特定应用的无障碍变乱时,可以通过此属性限定服务的范围。

4、启用无障碍服务

运行APP,设置 → 无障碍 → 找到我们的APP → 若显示关闭说明无障碍服务没启动点开同意

运行结果

当前页面:

Android Studio日志系统Logcat:

将存储于android的xml文件导出至PC:
        进入adb.exe的文件夹,在文件管理器地点栏输入cmd进入命令行。然后输入以下命令:
  1. adb pull /手机存储路径/文件名 本地路径
复制代码
        命令行界面如下:

xml文件如下:




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表