【玩转Android自动化】布局节点速查器

上一篇介绍了的这个系列的背景【玩转Android自动化】开篇序言 接下来就开始一步一步实现吧

工欲善其事必先利其器

  • 接触Android无障碍(AccessibilityService)功能开发首先遇到的问题就是如何获取页面上的元素信息,只有拿到元素唯一标识信息(比如元素id或者text)才能通过调用系统的findAccessibilityNodeInfosByViewId()findAccessibilityNodeInfosByText()去获取这个元素,然后对这个元素进行相关操作,比如常见的点击某个元素、获取某个元素的内容、滑动一下屏幕等类似的操作,通过这些操作就能组合成一条完整的业务流程。

如何获取元素

  • 常规获取页面元素的方式是使用Android Studio的LayoutInspector进行查看页面元素信息,但是这种方式有一个致命的问题就是APP要开启debug模式才可以获取到页面元素,所以要想分析其他市场上的APP是行不通的(不考虑模拟器或者root机),当然在历史的长河中还存在过Android Device MonitorUI Automator Viewer,这两个工具也可以获取到节点元素信息,不过基本上算是被淘汰了,这里就不展开讲了,感兴趣的可以网上搜搜有很多现成的资料

  • 本文要介绍的是直接通过代码获取当前屏幕窗口的根节点,然后一层一层的遍历,最后打印出自己需要的相对重要的节点信息即可,有了这么一套工具之后就可以对你想处理的页面可以很方便的获取页面元素,让自己只专注于具体的业务流程开发就可以了,可以节约大部分查找页面元素的时间。

具体实现

实现思路

AccessibilityService类中有个getRootInActiveWindow()方法,他可以获取到当前活动窗口的根节点,返回的是AccessibilityNodeInfo对象,然后可以拿到它的childCount,通过循环遍历就可以查找到所有的子节点了,节点包含的信息很多,我们一般需要是className、text、viewIdResourceName、description、isClickable、isScrollable、isEditable这些基础数据,为了方便打印出我们需要的数据,首先定义一个包装类NodeWrapper,重写它的toString()方法,这样就可以按照既定的格式输出一些我们需要的信息了。

data class NodeWrapper(
    var className: String,
    var text: String? = null,
    var id: String? = null,
    var description: String? = null,
    var isClickable: Boolean = false,
    var isScrollable: Boolean = false,
    var isEditable: Boolean = false,
    var nodeInfo: AccessibilityNodeInfo? = null
) {
    override fun toString() = "className = $className → text = $text → id = $id → description = $description → isClickable = $isClickable → isScrollable = $isScrollable → isEditable = $isEditable"

}
代码设计

我们把这个打印节点信息的方法定义为printNodeInfo(),因为rootInActiveWindow()返回是AccessibilityNodeInfo对象,所以为了方便调用我们定义一个AccessibilityNodeInfo类的扩展方法,大致内容如下:

fun AccessibilityNodeInfo?.printNodeInfo() {
    val node = this ?: return
    //选择我们需要的信息包装起来,便于打印
    val nodeWrapper = NodeWrapper(
        className = node.className.default(),
        text = node.text.default(),
        id = node.viewIdResourceName.default(),
        description = node.contentDescription.default(),
        isClickable = node.isClickable,
        isScrollable = node.isScrollable,
        isEditable = node.isEditable,
        nodeInfo = node
    )
    //打印我们需要的信息
    Log.d("printNodeInfo", nodeWrapper.toString())
    val size = node.childCount
    if (size > 0) {
        //通过递归调用去打印子节点的信息
        for (index in 0 until size) {
            node.getChild(index).printNodeInfo()
        }
    }
}
如何使用

首先需要定义一个继承AccessibilityService类的子类TestAccessibilityService类,重写onCreate()方法,在里边进行初始化赋值给我们定义的全局变量testAccessibilityService = this。

companion object {
    var testAccessibilityService: AccessibilityService? = null
}

override fun onCreate() {
    super.onCreate()
    testAccessibilityService = this
}

override fun onDestroy() {
    testAccessibilityService = null
    super.onDestroy()
}

然后在需要的地方就可以调用testAccessibilityService?.rootInActiveWindow?.printNodeInfo()进行打印节点信息了。先来看下效果吧。

图片

className = android.widget.FrameLayout → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.LinearLayout → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.FrameLayout → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.LinearLayout → text =  → id = com.android.wechat.tools:id/action_bar_root → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.FrameLayout → text =  → id = android:id/content → description =  → clickable = false → scrollable = false → editable = false
className = android.view.ViewGroup → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.LinearLayout → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.view.ViewGroup → text =  → id = com.android.wechat.tools:id/toolbar → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.TextView → text = 微信自动化工具 → id =  → description =  → clickable = false → scrollable = false → editable = false
className = androidx.appcompat.widget.LinearLayoutCompat → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.view.ViewGroup → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.FrameLayout → text =  → id = com.android.wechat.tools:id/nav_host_fragment_content_main → description =  → clickable = false → scrollable = false → editable = false
className = android.view.ViewGroup → text =  → id =  → description =  → clickable = false → scrollable = false → editable = false
className = android.widget.Button → text = 无障碍服务已开启 → id = com.android.wechat.tools:id/btn_open_service → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.Button → text = 页面元素检测工具 → id = com.android.wechat.tools:id/btn_print_node → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.Button → text = 获取微信好友列表 → id = com.android.wechat.tools:id/btn_get_friend_list → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.Button → text = 一键检测《通过假转账方式》 → id = com.android.wechat.tools:id/btn_check → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.Button → text = 一键检测《通过拉群方式》 → id = com.android.wechat.tools:id/btn_check_by_group → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.Button → text = 微信自动抢红包 → id = com.android.wechat.tools:id/btn_wx_auto_hb → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.Button → text = 微信自动回复消息 → id = com.android.wechat.tools:id/btn_wx_auto_reply → description =  → clickable = true → scrollable = false → editable = false
className = android.widget.TextView → text =  → id = com.android.wechat.tools:id/tv_task_des → description =  → clickable = false → scrollable = false → editable = false
className = androidx.recyclerview.widget.RecyclerView → text =  → id = com.android.wechat.tools:id/recycler_view → description =  → clickable = false → scrollable = false → editable = false
className = android.view.View → text =  → id = android:id/statusBarBackground → description =  → clickable = false → scrollable = false → editable = false

可以看到当前窗口的元素都已经被详细的打印出来了,不过这样打印出来看起来怪怪的,总感觉是哪里有问题,看了半天发现,输出的节点都是同一级的,看不出来父子元素的关系,这样就会出现我们想要查看某个元素的父节点的时候就特别费劲,如果页面简单还好,稍微复杂点的页面能把人看懵逼。那么该怎么去展示父子节点的关系呐,突然想到我们gradle中有个dependenciestask,这个task可以打印出出我们所依赖库中的具体依赖关系,也就是我们需要的父子关系,我们可以先看一下它输出的是什么样的。

+--- androidx.core:core-ktx:1.7.0
|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.31 -> 1.7.10 (*)
|    +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|    \--- androidx.core:core:1.7.0
|         +--- androidx.annotation:annotation:1.2.0 -> 1.3.0
|         +--- androidx.annotation:annotation-experimental:1.1.0
|         +--- androidx.lifecycle:lifecycle-runtime:2.3.1
|         |    +--- androidx.lifecycle:lifecycle-common:2.3.1
|         |    |    \--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|         |    +--- androidx.arch.core:core-common:2.1.0
|         |    |    \--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|         |    \--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|         \--- androidx.versionedparcelable:versionedparcelable:1.1.1
|              +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|              \--- androidx.collection:collection:1.0.0 -> 1.1.0
|                   \--- androidx.annotation:annotation:1.1.0 -> 1.3.0

可以清晰的看出层级关系,这不这个是我们需要的吗,先来分析一下他的结构,根节点+--- 开始标记,每个子节点前加个|区分,然后加几个空格让他出现父子关系的样子,如果子节点是最后一个再加个\---表示当前是层级的最后一个节点,以此类推就出现上边展示的输出格式了,接下来看看具体代码吧。

fun AccessibilityNodeInfo?.printNodeInfo(prefix: String = "", isLast: Boolean = false) {
    val node = this ?: return
    val nodeWrapper = NodeWrapper(
        text = node.text.default(),
        id = node.viewIdResourceName.default(),
        className = node.className.default(),
        description = node.contentDescription.default(),
        isClickable = node.isClickable,
        isScrollable = node.isScrollable,
        isEditable = node.isEditable,
        nodeInfo = node
    )
    val marker = if (isLast) """\--- """ else "+--- "
    val currentPrefix = "$prefix$marker"
    Log.d("printNodeInfo", currentPrefix + nodeWrapper.toString())

    val size = node.childCount
    if (size > 0) {
        val childPrefix = prefix + if (isLast) "  " else "|  "
        val lastChildIndex = size - 1
        for (index in 0 until size) {
            val isLastChild = index == lastChildIndex
            node.getChild(index).printNodeInfo(childPrefix, isLastChild)
        }
    }
}

代码不复杂,只是在原来代码的基础上多了几个前缀字符的标记而已,现在再打印一下同一个页面信息看看效果吧。

+--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |  \--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |    \--- className = android.widget.LinearLayout → text =  → id = com.android.wechat.tools:id/action_bar_root → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |      \--- className = android.widget.FrameLayout → text =  → id = android:id/content → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |        \--- className = android.view.ViewGroup → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |          +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |          |  \--- className = android.view.ViewGroup → text =  → id = com.android.wechat.tools:id/toolbar → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |          |    +--- className = android.widget.TextView → text = 微信自动化工具 → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |          |    \--- className = androidx.appcompat.widget.LinearLayoutCompat → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |          \--- className = android.view.ViewGroup → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |            \--- className = android.widget.FrameLayout → text =  → id = com.android.wechat.tools:id/nav_host_fragment_content_main → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |              \--- className = android.view.ViewGroup → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 无障碍服务已开启 → id = com.android.wechat.tools:id/btn_open_service → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 页面元素检测工具 → id = com.android.wechat.tools:id/btn_print_node → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 获取微信好友列表 → id = com.android.wechat.tools:id/btn_get_friend_list → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 一键检测《通过假转账方式》 → id = com.android.wechat.tools:id/btn_check → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 一键检测《通过拉群方式》 → id = com.android.wechat.tools:id/btn_check_by_group → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 微信自动抢红包 → id = com.android.wechat.tools:id/btn_wx_auto_hb → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.Button → text = 微信自动回复消息 → id = com.android.wechat.tools:id/btn_wx_auto_reply → description =  → isClickable = true → isScrollable = false → isEditable = false
|  |                +--- className = android.widget.TextView → text =  → id = com.android.wechat.tools:id/tv_task_des → description =  → isClickable = false → isScrollable = false → isEditable = false
|  |                \--- className = androidx.recyclerview.widget.RecyclerView → text =  → id = com.android.wechat.tools:id/recycler_view → description =  → isClickable = false → isScrollable = false → isEditable = false
|  \--- className = android.view.View → text =  → id = android:id/statusBarBackground → description =  → isClickable = false → isScrollable = false → isEditable = false

哈哈,是不是瞬间好看多了,各个父子关系可以清晰的看出来,同级元素页可以一眼看出来了。

因为getRootInActiveWindow()是在AccessibilityService类中才有的方法,每次调用需要写一些重复性的代码,所以我们再定义一个AccessibilityService类的扩展方法,以后直接调用xxx.printNodeInfo()就可以了。

fun AccessibilityService?.printNodeInfo() {
    this ?: return
    rootInActiveWindow.printNodeInfo()
}
拓展

虽然打印的代码部分完成了,这个时候又引出一个问题,比如我们想在微信里边看某个页面的节点信息怎么办呐,总不能每次都编译一遍代码去执行打印节点的方法吧,我们期望的是在任何页面都可以随时随地的打印,如何在微信页面触发我们的printNodeInfo()方法呐,自然而然的想到用悬浮窗去实现了,悬浮窗可以浮在任何页面之上,我们只需点一下悬浮窗,然它获取AccessibilityService对象并调一下printNodeInfo()方法就可以了,关于Android悬浮窗这里就不做过多讲解了,因为不是本文的重点,在网上找了优秀的开源库直接使用就可以了。我们看一下微信的页面信息吧。

+--- className = android.widget.FrameLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|  \--- className = android.widget.FrameLayout → text =  → id = com.tencent.mm:id/fkh → description =  → isClickable = false → isScrollable = false → isEditable = false
|    \--- className = android.widget.RelativeLayout → text =  → id = com.tencent.mm:id/gv5 → description =  → isClickable = false → isScrollable = false → isEditable = false
|      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/gv4 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        +--- className = android.widget.RelativeLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  +--- className = android.widget.ListView → text =  → id = android:id/list → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/l49 → description =  → isClickable = true → isScrollable = false → isEditable = false
|        |  |  |  +--- className = android.widget.ImageView → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |  +--- className = android.widget.TextView → text = 微信 → id = com.tencent.mm:id/b7 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |  \--- className = android.widget.TextView → text = Version 8.0.40 → id = com.tencent.mm:id/b6 → description =  → isClickable = true → isScrollable = false → isEditable = false
|        |  |  +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/iwg → description =  → isClickable = true → isScrollable = false → isEditable = false
|        |  |  |  +--- className = android.view.View → text =  → id = com.tencent.mm:id/krm → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/br8 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/gv6 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/kp3 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        |    \--- className = android.widget.TextView → text = 功能介绍 → id = android:id/title → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/its → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |      \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/isy → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/iwg → description =  → isClickable = true → isScrollable = false → isEditable = false
|        |  |  |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/br8 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |  \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/kp3 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |    \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |      \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        \--- className = android.widget.TextView → text = 投诉 → id = android:id/title → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/its → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |      \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/isy → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/iwg → description =  → isClickable = true → isScrollable = false → isEditable = false
|        |  |  |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    +--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/br8 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/gv6 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |      \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/kp3 → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        |  \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        |    \--- className = android.widget.TextView → text = 检查新版本 → id = android:id/title → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    |        \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |    \--- className = android.widget.LinearLayout → text =  → id = com.tencent.mm:id/its → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  |      \--- className = android.widget.ImageView → text =  → id = com.tencent.mm:id/isy → description =  → isClickable = false → isScrollable = false → isEditable = false
|        |  |  \--- className = android.widget.TextView → text =  → id = android:id/title → description =  → isClickable = true → isScrollable = false → isEditable = false
|        |  \--- className = android.view.View → text =  → id = com.tencent.mm:id/e5c → description =  → isClickable = false → isScrollable = false → isEditable = false
|        \--- className = android.widget.FrameLayout → text =  → id = com.tencent.mm:id/i1f → description =  → isClickable = false → isScrollable = false → isEditable = false
|          \--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|            +--- className = android.widget.TextView → text = 《软件许可及服务协议》 → id = com.tencent.mm:id/khc → description =  → isClickable = true → isScrollable = false → isEditable = false
|            +--- className = android.widget.LinearLayout → text =  → id =  → description =  → isClickable = false → isScrollable = false → isEditable = false
|            |  +--- className = android.widget.TextView → text = 《隐私保护指引摘要》 → id = com.tencent.mm:id/khb → description =  → isClickable = true → isScrollable = false → isEditable = false
|            |  \--- className = android.widget.TextView → text = 《隐私保护指引》 → id = com.tencent.mm:id/kha → description =  → isClickable = true → isScrollable = false → isEditable = false
|            +--- className = android.widget.TextView → text = 客服电话:400 670 0700 → id = com.tencent.mm:id/exh → description =  → isClickable = false → isScrollable = false → isEditable = false
|            \--- className = android.widget.TextView → text = 腾讯公司 版权所有

------->以下是新增功能<-------

对布局速查器相关做了重构,抽离成一个单独的module了。

PC端查看数据(你相信光吗)

  • 上边介绍了通过悬浮球去打印信息,后来对打印的数据也做了简化处理,但是对使用者来说还是不太方便,弊端有两点,一是需要手机连接到Android Studio上通过查看log的方式查看节点信息,这点在后来的实际场景中感觉很鸡肋,二是即使后来我也加了在手机端直接查看的功能,但移动端终究是小屏,看起来很费劲,而且没法搜索查找。基于以上两点,连夜重构了这部分代码,让我们的数据可以在电脑端浏览器里直接操作查看。

  • 要怎么实现手机与PC端通信呐,这里就用到去年我写的一个Android本地服务AndroidLocalService库了,真没想到在这里又用到它了。对这个库不了解的可以看下源码或者看下我之前写的Android抓包从未如此简单这边文章中也用到了AndroidLocalService并且对其进行简单的介绍。

  • 借助于AndroidLocalService,我们需要写一个html页面用于PC端浏览器预览和操作,具体的html页面编写说实话我不太擅长,偷个懒就让ChatGPT帮我简单生成了一段代码,经过简单的修改就可以正常使用了,哈哈。

chatGPT.png

写好了页面我们就把页面信息和对本地的读写操作都写在通过AndroidLocalService实现的AutoToolsService类中,这样一个本地服务就搭建好了,大致内容如下:

@Page("")
fun getIndexPage() = "auto_tools_index.html"

@Get("getPageSimpleNodeInfo")
fun getPageSimpleNodeInfo(): String {
    ContentManger.printContent =
        AutoToolsAccessibility.autoToolsAccessibility?.printNodeInfo(true)
    return ContentManger.printContent
        ?: "暂未获取到页面节点信息,请先确保APP已开启【布局节点速查器】无障碍功能"
}

有了本地服务之后就是初始化和启动服务了。

ALSHelper.init(requireContext().applicationContext)
ALSHelper.startService(ServiceConfig(AutoToolsService::class.java))

然后就让我们在电脑端点击一下按钮,看看最终效果吧。

既然PC端页面那么大,干脆就多做点功能吧,增加:【上】【下】【左】【右】四个方向滑动屏幕、【返回】键【点击】指定节点【输入】内容,如果后边有其他需要的功能后续再添加吧!

  • 再来看下移动端效果吧

手机端展示效果.jpg

最后

  • 这样我们的元素检测小工具就算全部写完了,以后想看某个页面查看节点信息就可以直接打开我们的APP,开启无障碍服务后,启动悬浮窗,打开指定APP某个页面,点一下悬浮窗就可以在控制台看到美化后的具体的节点信息了。

  • 后续如何有需求我们可以把打印的节点信息直接在APP中查看,但是看出节点这种事情还是在PC端大屏上看着才舒服,所以暂时就不展示在手机端喽

  • 刀已磨好,接下来就开始在微信中实操吧

感兴趣的可以下载demo体验一下,在阅读源码过程遇到任何问题欢迎提Issues,如果对你有帮助,希望动动你的发财小手点个赞呗

预告

下一篇让我们期待一下【玩转Android自动化】小试牛刀,通过一个简单的例子带大家实操一下

作者:lygttpod
链接:https://juejin.cn/post/7266087487939035192
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值