提高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
相关文章
|
9天前
|
运维 Prometheus 监控
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
如何在测试环境中保持操作系统、浏览器版本和服务器配置的稳定性和一致性?
|
29天前
|
测试技术 API 项目管理
API测试方法
【10月更文挑战第18天】API测试方法
45 1
|
1月前
|
机器学习/深度学习 人工智能 监控
提升软件质量的关键路径:高效测试策略与实践在软件开发的宇宙中,每一行代码都如同星辰般璀璨,而将这些星辰编织成星系的过程,则依赖于严谨而高效的测试策略。本文将引领读者探索软件测试的奥秘,揭示如何通过精心设计的测试方案,不仅提升软件的性能与稳定性,还能加速产品上市的步伐,最终实现质量与效率的双重飞跃。
在软件工程的浩瀚星海中,测试不仅是发现缺陷的放大镜,更是保障软件质量的坚固防线。本文旨在探讨一种高效且创新的软件测试策略框架,它融合了传统方法的精髓与现代技术的突破,旨在为软件开发团队提供一套系统化、可执行性强的测试指引。我们将从测试规划的起点出发,沿着测试设计、执行、反馈再到持续优化的轨迹,逐步展开论述。每一步都强调实用性与前瞻性相结合,确保测试活动能够紧跟软件开发的步伐,及时适应变化,有效应对各种挑战。
|
1月前
|
安全 测试技术
北大李戈团队提出大模型单测生成新方法,显著提升代码测试覆盖率
【10月更文挑战第1天】北京大学李戈教授团队提出了一种名为“统一生成测试”的创新方法,有效提升了大模型如GPT-2和GPT-3在单一测试中的代码生成覆盖率,分别从56%提升至72%和从61%提升至78%。这种方法结合了模糊测试、变异测试和生成对抗网络等多种技术,克服了传统测试方法的局限性,在大模型测试领域实现了重要突破,有助于提高系统的可靠性和安全性。然而,该方法的实现复杂度较高且实际应用效果仍需进一步验证。论文可从此链接下载:【https://drive.weixin.qq.com/s?k=ACAAewd0AA48Z2kXrJ】
62 1
|
27天前
|
测试技术 UED
软件测试中的“灰盒”方法:一种平衡透明度与效率的策略
在软件开发的复杂世界中,确保产品质量和用户体验至关重要。本文将探讨一种被称为“灰盒测试”的方法,它结合了白盒和黑盒测试的优点,旨在提高测试效率同时保持一定程度的透明度。我们将通过具体案例分析,展示灰盒测试如何在实际工作中发挥作用,并讨论其对现代软件开发流程的影响。
|
8天前
|
存储 监控 前端开发
如何确保测试脚本的稳定性和可靠性?
确保测试脚本的稳定性和可靠性是保证性能测试结果准确有效的关键
|
1月前
|
存储 监控 网络协议
服务器压力测试是一种评估系统在极端条件下的表现和稳定性的技术
【10月更文挑战第11天】服务器压力测试是一种评估系统在极端条件下的表现和稳定性的技术
112 32
|
19天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
35 2
|
26天前
|
监控 Devops 持续交付
掌握 GitOps:实现 DevOps 自动化的现代方法
【10月更文挑战第19天】GitOps 是一种基于 Git 仓库管理应用配置和集群状态的现代化 DevOps 方法,通过自动化工具实现声明式配置和持续部署。本文介绍了 GitOps 的核心概念、优势、挑战及实施的最佳实践,帮助团队提高部署效率和系统可靠性。
|
1月前
|
测试技术 Python
自动化测试项目学习笔记(三):Unittest加载测试用例的四种方法
本文介绍了使用Python的unittest框架来加载测试用例的四种方法,包括通过测试用例类、模块、路径和逐条加载测试用例。
62 0
自动化测试项目学习笔记(三):Unittest加载测试用例的四种方法

热门文章

最新文章

下一篇
无影云桌面