提高Android自动化测试稳定性的方法(三)

简介: 在之前的一篇文章《移动端UI自动化过程中的难点及应对策略》中,我们提到在Android自动化测试执行过程中经常会遇到一些非预期的系统弹框,我们可以通过无障碍服务来实现智能点击处理,但是通常这个服务只能手动到设置中开启,今天就跟大家分享一下如何实现一个自定义的无障碍服务以及如何自动化的开启它。

实现自定义的无障碍服务



自定义一个服务继承自AccessibilityService

package com.android.jarvis.accessibility
import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.AccessibilityServiceInfo
import android.util.Log
import android.view.KeyEvent
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
class JarvisAccessibilityService : AccessibilityService() {
    public override fun onServiceConnected() {
        Log.i(TAG, "onServiceConnected: ")
        val accessibilityServiceInfo = AccessibilityServiceInfo()
        accessibilityServiceInfo.packageNames = null // 监听所有应用
        accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK //监听哪些行为
        accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK //反馈
        accessibilityServiceInfo.notificationTimeout = 200
        serviceInfo = accessibilityServiceInfo
    }
    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        traverseNode(event.source)
        processAccessibilityEvent(event)
    }
    private fun processAccessibilityEvent(event: AccessibilityEvent) {
        processBlockingNotification(event)
        if (rootInActiveWindow == null) {
            Log.i(TAG, "AccessibilityNodeInfo = null")
            return
        }
        traverseNode(rootInActiveWindow)
    }
    private fun processBlockingNotification(event: AccessibilityEvent) {
        val node = event.source
        if (node != null) {
            if (findblockingUI("是否允许 USB 调试?", node)) {
                findAndPerformCheck("始终允许使用这台计算机进行调试", node)
                findAndPerformAction("确定", node)
            }
            if (findblockingUI("是否允许USB调试?", node)) {
                findAndPerformCheck("始终允许使用这台计算机进行调试", node)
                findAndPerformAction("确定", node)
            }
            if (findblockingUI("允许USB调试吗?", node)) {
                findAndPerformCheck("一律允许使用这台计算机进行调试", node)
                findAndPerformAction("确定", node)
            }
            if (findblockingUI("允许 USB 调试吗?", node)) {
                findAndPerformCheck("一律允许使用这台计算机进行调试", node)
                findAndPerformAction("允许", node)
            }
            if (findblockingUI("允许 USB 调试吗?", node)) {
                findAndPerformCheck("一律允许使用这台计算机进行调试", node)
                findAndPerformAction("确定", node)
            }
        }
    }
    private fun findblockingUI(text: String, source: AccessibilityNodeInfo): Boolean {
        val nodes = source.findAccessibilityNodeInfosByText(text)
        if (nodes == null || nodes.isEmpty()) {
            return false
        }
        Log.d(TAG, "findblockingUI $text")
        return true
    }
    public override fun onKeyEvent(event: KeyEvent): Boolean {
        return true
    }
    override fun onInterrupt() {
        Log.e(TAG, "服务被Interrupt")
    }
    private fun traverseNode(node: AccessibilityNodeInfo?) {
        if (node != null) {
            val count = node.childCount
            if (count > 0) {
                for (i in 0 until count) {
                    traverseNode(node.getChild(i))
                }
                return
            }
            val clickable = node.isClickable
            val text = node.text
            val pkgName = node.packageName
            if (!"com.miui.home".contentEquals(pkgName)) {
                Log.i(
                    TAG,
                    "pkg:" + pkgName as Any + " Node:" + text as Any + " clickable:" + clickable
                )
            }
        }
    }
    private fun findAndPerformAction(text: String, source: AccessibilityNodeInfo?): Int {
        if (source == null) {
            return 0
        }
        val nodes = source.findAccessibilityNodeInfosByText(text)
        var count = 0
        if (nodes != null && !nodes.isEmpty()) {
            for (i in nodes.indices) {
                if (performActionClick(nodes[i], text)) {
                    count++
                }
            }
        }
        return count
    }
    private fun performActionClick(node: AccessibilityNodeInfo?, text: String): Boolean {
        if (node == null) {
            return false
        }
        if (!isButton(node) && !isTextView(node) && !isView(node)) {
            return false
        }
        node.performAction(16)
        return true
    }
    private fun findAndPerformCheck(text: String, source: AccessibilityNodeInfo?) {
        if (source != null) {
            val nodes = source.findAccessibilityNodeInfosByText(text)
            if (nodes != null && !nodes.isEmpty()) {
                for (i in nodes.indices) {
                    Log.d(TAG, "performCheck $text")
                    performActionCheck(nodes[i])
                }
            }
        }
    }
    private fun performActionCheck(node: AccessibilityNodeInfo?) {
        if (node != null && isCheckBox(node) && !node.isChecked) {
            node.performAction(16)
        }
    }
    private fun isButton(node: AccessibilityNodeInfo): Boolean {
        return node.className == "android.widget.Button" || node.className == "amigo.widget.AmigoButton"
    }
    private fun isTextView(node: AccessibilityNodeInfo): Boolean {
        return node.className == "android.widget.TextView"
    }
    private fun isView(node: AccessibilityNodeInfo): Boolean {
        return node.className == "android.widget.View"
    }
    private fun isCheckBox(node: AccessibilityNodeInfo): Boolean {
        return node.className == "android.widget.CheckBox"
    }
    private fun isCheckedTextView(node: AccessibilityNodeInfo): Boolean {
        return node.className == "android.widget.CheckedTextView"
    }
    companion object {
        private const val TAG = "JarvisAccessibility"
    }
}

配置

在res/xml目录下新建accessibility_service_config.xml文件,如下:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeViewClicked|typeViewLongClicked|typeViewSelected|typeViewFocused|typeViewTextChanged|typeWindowStateChanged|typeNotificationStateChanged|typeViewHoverEnter|typeViewHoverExit|typeTouchExplorationGestureStart|typeTouchExplorationGestureEnd|typeWindowContentChanged|typeViewScrolled|typeViewTextSelectionChanged|typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:canRetrieveWindowContent="true"
    android:notificationTimeout="200" />

在AndroidManifest.xml中注册服务

 <service
            android:name=".accessibility.JarvisAccessibilityService"
            android:label="智能辅助服务"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter android:priority="2147483647">
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />
        </service>

自动开启无障碍服务

可以通过执行下面的命令就可以自动开启指定的无障碍服务:

adb shell content call --uri content://settings/secure --method PUT_secure --arg enabled_accessibility_services  --extra _user:i:0 --extra value:s:com.android.jarvis/com.android.jarvis.accessibility.JarvisAccessibilityService
adb shell content call --uri content://settings/secure --method PUT_secure --arg accessibility_enabled  --extra _user:i:0 --extra value:s:1
adb shell settings put secure enabled_accessibility_services com.android.jarvis/com.android.jarvis.accessibility.JarvisAccessibilityService
adb shell settings put secure accessibility_enabled 1
目录
打赏
0
0
0
0
5
分享
相关文章
浅谈网页端IM技术及相关测试方法实践(包括WebSocket性能测试)
最开始转转的客服系统体系如IM、工单以及机器人等都是使用第三方的产品。但第三方产品对于转转的业务,以及客服的效率等都产生了诸多限制,所以我们决定自研替换第三方系统。下面主要分享一下网页端IM技术及相关测试方法,我们先从了解IM系统和WebSocket开始。
83 4
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
130 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
AxBench:斯坦福大学推出评估语言模型控制方法的基准测试框架
AxBench 是由斯坦福大学推出,用于评估语言模型可解释性方法的基准测试框架,支持概念检测和模型转向任务,帮助研究者系统地比较不同控制技术的有效性。
55 5
AxBench:斯坦福大学推出评估语言模型控制方法的基准测试框架
API测试方法
【10月更文挑战第18天】API测试方法
118 1
提升软件质量的关键路径:高效测试策略与实践在软件开发的宇宙中,每一行代码都如同星辰般璀璨,而将这些星辰编织成星系的过程,则依赖于严谨而高效的测试策略。本文将引领读者探索软件测试的奥秘,揭示如何通过精心设计的测试方案,不仅提升软件的性能与稳定性,还能加速产品上市的步伐,最终实现质量与效率的双重飞跃。
在软件工程的浩瀚星海中,测试不仅是发现缺陷的放大镜,更是保障软件质量的坚固防线。本文旨在探讨一种高效且创新的软件测试策略框架,它融合了传统方法的精髓与现代技术的突破,旨在为软件开发团队提供一套系统化、可执行性强的测试指引。我们将从测试规划的起点出发,沿着测试设计、执行、反馈再到持续优化的轨迹,逐步展开论述。每一步都强调实用性与前瞻性相结合,确保测试活动能够紧跟软件开发的步伐,及时适应变化,有效应对各种挑战。
软件测试中的“灰盒”方法:一种平衡透明度与效率的策略
在软件开发的复杂世界中,确保产品质量和用户体验至关重要。本文将探讨一种被称为“灰盒测试”的方法,它结合了白盒和黑盒测试的优点,旨在提高测试效率同时保持一定程度的透明度。我们将通过具体案例分析,展示灰盒测试如何在实际工作中发挥作用,并讨论其对现代软件开发流程的影响。
如何确保测试脚本的稳定性和可靠性?
确保测试脚本的稳定性和可靠性是保证性能测试结果准确有效的关键
146 58
|
5月前
|
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
164 15
Android 系统缓存扫描与清理方法分析
在数据驱动时代,A/B 测试成为评估机器学习项目不同方案效果的重要方法
在数据驱动时代,A/B 测试成为评估机器学习项目不同方案效果的重要方法。本文介绍 A/B 测试的基本概念、步骤及其在模型评估、算法改进、特征选择和用户体验优化中的应用,同时提供 Python 实现示例,强调其在确保项目性能和用户体验方面的关键作用。
96 6

热门文章

最新文章