Appium自动化常用adb操作封装

一、前置说明

在Appium自动化中,经常需要使用adb命令与设备进行交互,所以有必要把常用的adb操作封装成一个类

二、代码实现

import os
import platform
import re
import subprocessfrom common import path
from common.exception import AndroidSDKUninstalledError, AndroidDevicesNotFoundError
from common.logger import loggerclass ADBRunner:def __init__(self):self._check_adb_is_installed()@staticmethoddef run_adb(command):try:result = subprocess.run(command, capture_output=True, text=True, check=True, shell=True)logger.debug(f"Execute adb command: {command}")return result.stdout.strip()except subprocess.CalledProcessError as e:logger.error(f"Execute adb command failure: {e}")return Nonedef _check_adb_is_installed(self):result = self.run_adb("adb --version")if not result:raise AndroidSDKUninstalledError('Android SDK is not installed or configured.')return resultdef adb_connect_device(self, device):return self.run_adb(f'adb connect {device}')def get_connected_device_udids(self):"""获取所有连接设备的序列号udid"""res = self.run_adb('adb devices')pattern = r'\b((?!of\b)\S+)\s+device'devices = re.findall(pattern, res)if not devices:raise AndroidDevicesNotFoundError('No connected mobile devices found.')logger.info(f'Devices found: {devices}')return devices@propertydef _grep(self):if platform.system() == 'Windows':return 'findstr'else:return 'grep'def get_activities(self, udid=None):"""获取当前设备的所有top activities, 输出结果示例:['com.android.settings/.Settings c4e2e17 pid=3637','com.mumu.launcher/.Launcher 9392da7 pid=1434','com.android.browser/com.android.settings de68b73 pid=3722']"""if not udid:udid = self.get_connected_device_udids()[0]command = f'adb -s {udid} shell dumpsys activity top | {self._grep} ACTIVITY'tops = self.run_adb(command).split('ACTIVITY')tops = [top.strip(' ').strip('\n') for top in tops if top]logger.debug(f'The top activities on device {udid} are: {tops}')return topsdef get_app_package_and_activity(self, udid=None):"""从 com.android.settings/.Settings c4e2e17 pid=3637,获取包名和活动页面名称输出:['com.android.settings', '.Settings']用途:capabilities = {"platformName": "Android","automationName": "uiautomator2","deviceName": "9YS0220306003185","appPackage": "com.tencent.mm",  # 包名"appActivity": ".ui.LauncherUI",  # 活动页面名称}"""last_activity_info = self.get_activities(udid)[-1]pattern = r'(\S+) (\S+) pid=(\d+)'match = re.match(pattern, last_activity_info)if match:package, activity = match.group(1).split('/')return [package, activity]else:raisedef get_apk_path(self, package_name, udid=None):"""从设备中使用包名,获取应用程序的APK路径。"""if not udid:udid = self.get_connected_device_udids()[0]# 使用pm path命令获取应用程序的APK路径command = f'adb -s {udid} shell pm path {package_name}'result = self.run_adb(command)if result and result.startswith('package:'):apk_path = result.replace('package:', '').strip()return apk_pathelse:logger.error(f"APK path for package '{package_name}' not found on the device {udid}.")return Nonedef get_apk_version(self, package_name, udid=None):"""获取应用程序的版本号。"""if not udid:udid = self.get_connected_device_udids()[0]# 使用dumpsys package命令获取应用程序的版本号command = f'adb -s {udid} shell dumpsys package {package_name} | {self._grep} versionCode'result = self.run_adb(command)match = re.search(r'versionCode=(\d+)', result)if match:version_code = match.group(1)return version_codeelse:logger.error(f"Version code not found for package '{package_name}'.")return Nonedef pull_apk_from_device(self, package_name, output_dir=None, apk_name=None, udid=None):"""根据包名从当前设备中将应用程序的APK文件复制至本地。用于:capabilities = {"platformName": "Android","automationName": "uiautomator2","deviceName": "9YS0220306003185","app": apk_path,  # 用在这里# "appPackage": "com.tencent.mm",# "appActivity": ".ui.LauncherUI",}"""if not udid:udid = self.get_connected_device_udids()[0]if not output_dir:output_dir = path.get_apk_resources_dir()# 获取应用程序的APK路径apk_path = self.get_apk_path(package_name)apk_name = apk_name if apk_name else package_nameapk_version = self.get_apk_version(package_name)if apk_path:# 构建本地输出路径local_output_path = os.path.join(output_dir, f"{apk_name}_{apk_version}.apk")# 使用adb pull命令将APK复制到本地command = f'adb -s {udid} pull {apk_path} {local_output_path}'result = self.run_adb(command)if result:logger.info(f"APK successfully pulled to: {local_output_path}")return local_output_pathelse:logger.error("Failed to pull APK from device.")if __name__ == '__main__':adb = ADBRunner()print(adb.get_connected_device_udids())print(adb.get_app_package_and_activity())print(adb.get_apk_path('cn.com.open.mooc'))print(adb.get_apk_version('cn.com.open.mooc'))print(adb.pull_apk_from_device('cn.com.open.mooc'))

欢迎技术交流:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/217355.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

语音识别功能测试:90%问题,可以通过技术解决

现在市面上的智能电子产品千千万,为了达到人们使用更加方便的目的,很多智能产品都开发了语音识别功能,用来语音唤醒进行交互;另外,各大公司也开发出来了各种智能语音机器人,比如小米公司的“小爱”&#xf…

DHCP—动态主机配置协议

动态主机配置协议DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是RFC 1541(已被RFC 2131取代)定义的标准协议,该协议允许服务器向客户端动态分配IP地址和配置信息。 DHCP协议支持C/S&#x…

外汇天眼:Coinbase国际交易所将启动现货市场

Coinbase宣布了Coinbase国际交易所扩张的下一阶段——退出符合条件客户的非美国现货市场。 这一最新发展旨在满足Coinbase全球用户群体的独特需求和需求,同时强化其扩大国际访问可信产品和服务的战略使命。 Coinbase国际交易所现货交易的推出和扩展将分阶段进行。1…

vite+vue3+electron搭建项目

编辑器使用vscode,打开一个空文件夹 第一步 初始化vite项目 初始化vite项目,命令 npm init vite 第二步 下载依赖 进入新建的项目,下载依赖,命令 cd vite-projec npm i第三步 使用cnpm下载 electron依赖 新建一个终端&#…

05 python数据容器

5.1 数据容器认识 5.2 python列表 5.2.1 列表的定义 演示数据容器之:list 语法:[元素,元素,....] #定义一个列表List List [itheima,uityu,gsdfg] List1 [itheima,6666,True] print(List) print(List1) print(type(List)) pr…

综合实验:期末

实验要求: 一.物理连接 实验分2个组进行,使用思科模拟软件。每个同学模拟两个组。每个组选用一台路由器、一台三层交换机和一台二层交换机。要求按下图拓扑进行连接。如下图:最上端设备为核心交换机,按老师要求配置&a…

实验:BGP配置

1.实验目的: 本实验旨在掌握BGP协议的基本概念和配置方法,以及使用Packet Tracer模拟网络环境进行BGP配置的方法。 2.实验要求: 理解BGP协议的基本概念和原理;掌握BGP协议的配置方法;能够使用Packet Tracer模拟网络…

2019年第八届数学建模国际赛小美赛B题数据中心冷出风口的设计解题全过程文档及程序

2019年第八届数学建模国际赛小美赛 B题 数据中心冷出风口的设计 原题再现: 这是数据中心空调设计面临的一个问题。在一些数据中心,计算机机柜是开放的,在一个房间里排列成三到四排。冷却后的空气通过主管进入房间,并分为三到四个…

华为交换机——配置策略路由(基于IP地址)示例

一、组网需求: 汇聚层Switch做三层转发设备,接入层设备LSW做用户网关,接入层LSW和汇聚层Switch之间路由可达。汇聚层Switch通过两条链路连接到两个核心路由器上,一条是高速链路,网关为10.1.20.1/24;另外一…

【Hive】——DDL(DATABASE)

1 概述 2 创建数据库 create database if not exists test_database comment "this is my first db" with dbproperties (createdByAllen);3 描述数据库信息 describe 可以简写为desc extended 可以展示更多信息 describe database test_database; describe databa…

案例058:基于微信小程序的智能社区服务管理系统

文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…

C++类与对象(一)

目录 一,面向过程和面向对象初步认识 二,类的引入 三,类的定义 四,类的访问限定符及封装 五,类的实例化 六,类对象模型 七,this指针 一,面向过程和面向对象初步认识 c语言是面…

超聚变服务器(原华为服务器)网站模拟器

一、超聚变服务器(原华为服务器)网站模拟器: 原来了解服务器可以从他的网站上进行了解,模拟器做的很好了。 https://support.xfusion.com/server-simulators/ 有很多的模拟器,今天主要看下BMC的设置 有很多的在线工具…

软考机考考试第一批经验分享

由于机考的特殊性,考试环境与传统笔试环境有所不同。下面是与考试环境相关的总结: 草稿纸:考场提供足够数量的草稿纸,每位考生都会分发一张白纸作为草稿纸。在草稿纸上需要写上准考证号。如果不够用,可以向监考老师再次…

java springboot+jsoup写一段爬虫脚本 将指定地址的 图片链接 文本 超链接地址存入自己的属性类对象中

首先 还是最基本的 要在 pom.xml 引入依赖 <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.14.1</version> </dependency>然后 我们可以在项目中创建一个属性类 我这里就叫 WebContent了…

自己动手写数据库: select 查询语句对应查询树的构造和执行

首先我们需要给原来代码打个补丁&#xff0c;在SelectScan 结构体初始化时需要传入 UpdateScan 接口对象&#xff0c;但很多时候我们需要传入的是 Scan 对象&#xff0c;因此我们需要做一个转换&#xff0c;也就是当初始化 SelectScan 时&#xff0c;如果传入的是 Scan 对象&am…

JWT令牌的作用和生成

JWT令牌&#xff08;JSON Web Token&#xff09;是一种用于身份验证和授权的安全令牌。它由三部分组成&#xff1a;头部、载荷和签名。 JWT令牌的作用如下&#xff1a; 身份验证&#xff1a;JWT令牌可以验证用户身份。当用户登录后&#xff0c;服务器会生成一个JWT令牌并返回…

小白学开源

第一步 访问程序员交友网站&#xff0c;注册GitHub账号&#xff0c;注册流程请自行了解&#xff0c;如果已有账户请登录 第二步 在搜索框输入你想查找的项目&#xff0c;例如cjson 第三步 选择你感兴趣的那个项目&#xff0c;这里选择排行最前的那个&#xff0c;它的收藏数有…

electron中获取mac地址

electron中获取mac地址 引入为了方便做单点登录&#xff0c;我们往往需要使用某个唯一标识来标记客户端的设备&#xff0c;mac地址就是一个还不错的选择思路 我们可以使用Node.js的内置模块os&#xff0c;调用其中的networkInterfaces方法。该方法会返回一个包含网络接口信息…

Navicat 技术指引 | 适用于 GaussDB 分布式的备份/还原功能

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…